fake va_list in ARC

I need to create in an iOS application a fake va_list to pass to a NSString initWithFormat:arguments: function, this is my code:

NSArray *fixedArguments = [[NSArray alloc] initWithArray:arguments]; 

NSRange range = NSMakeRange(0, [fixedArguments count]);

va_list fakeArgList = (va_list)malloc(sizeof(NSString *) * [fixedArguments count]);

__unsafe_unretained id *ptr = (__unsafe_unretained id *)fakeArgList;

[fixedArguments getObjects:ptr range:range];

content = [[NSString alloc] initWithFormat:outputFormat
                                            arguments:(va_list)fakeArgList];
free(fakeArgList);

The compiler complains with this message on the cast line:

  • Find if current time is between a range
  • NSCache initialization for storing UIImage ios
  • iPhone/iOS: How Do I Tell What Localization The Phone is Using At Runtime?
  • Xcode 7.0 XCTest dyld: could not load inserted library IDEBundleInjection
  • Xcode 6 Beta7 NSDictionary to Swift
  • iOS 7 - Status bar overlaps the view
  • error: cast of a non-Objective-C pointer type 'va_list' (aka 'char *') to '__unsafe_unretained id *' is disallowed with ARC
    

    The getObjects:range: function is defined as follows:

    - (void)getObjects:(id __unsafe_unretained [])objects range:(NSRange)range;
    

    I’ve tried everything but still can’t get rid of this error…

    Is there a solution for creating a fake va_list with ARC enabled? What am i doing wrong?

    2 Solutions Collect From Internet About “fake va_list in ARC”

    EDIT: This no longer works. As foreseen in the initial answer, the ABI appears to have changed out from under this answer

    Played around for a bit and got it to work — Double checked for leaks or abandoned memory and didn’t see any.

        NSArray *fixedArguments = [[NSArray alloc] initWithObjects: @"foo", @"bar", @"baz", nil]; 
    
        NSRange range = NSMakeRange(0, [fixedArguments count]);
    
        NSMutableData* data = [NSMutableData dataWithLength: sizeof(id) * [fixedArguments count]];    
    
        [fixedArguments getObjects: (__unsafe_unretained id *)data.mutableBytes range:range];
    
        NSString* content = [[NSString alloc] initWithFormat: @"1: %@ 2: %@ 3: %@"  arguments: data.mutableBytes];
    
        NSLog(@"%@", content);
    

    I like to (ab)use NSMutableData like this to get retain/release semantics on an arbitrary chunk of memory — It’s not necessarily relevant to the issue at hand, but it’s a neat little trick.

    As a note to future readers: Faking up a va_list like this happens to work with the current ABI for MacOS and iOS, but in general it’s not portable, and not a good approach.

    Its possible if you are willing to add a little bit of swift to your project!

    The important bit is the mapping of NSArray to [CVarArgType] which is the swift equivalent for va_list. If you try to cast [AnyObject] to [CVarArgType] you cause run time crashes, but with the map we can explicitly make the needed list.

    The rest of the code is the wrapper I made so that I can call this from obj-c. You could make a wrapper for any obj-c function that you want to call in this way.

    @objc class StringFormat: NSObject {
        class func format(key: String, args: [AnyObject]) -> String {
            let locArgs: [CVarArgType] = args.map({ (arg: AnyObject) -> CVarArgType in
                if let iArg = (arg is NSNumber ? arg.intValue : nil) {
                    return iArg
                }
                return arg as! CVarArgType
            });
            return String(format: key, arguments: locArgs)
        }
    }