Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting NSArray Contents to a varargs (With ARC) For Use With NSString initWithFormat

We have some code today that takes an NSArray and passes it as a argument list to -[NSString initWithFormat:arguments] and we're trying to get this to work with ARC. Here's the code were using

NSString* format = @"Item %s and Item %s"; // Retrieved elsewhere
NSArray* args = [NSArray arrayWithObjects:@"1", @"2", nil]; // Retrieved elsewhere

// http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html
char* argsList = (char*) malloc(sizeof(NSString*) * args.count);
[args getObjects:(id*) argsList];
NSString* message = [[[NSString alloc] initWithFormat:format arguments:argsList] autorelease];
free(argsList);

Any recommendations on how to make this ARC compliant? Or we're even open to a better way of doing it.

like image 376
Gary Rudolph Avatar asked Nov 25 '11 19:11

Gary Rudolph


2 Answers

This only works for arrays with a single element

The answer by chrisco was working well, until I went to compile with 64-bit architecture. This caused an error:

EXC_BAD_ADDRESS type EXC_I386_GPFLT

The solution was to use a slightly different approach for passing the argument list to the method:

+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments;
{
     __unsafe_unretained id  * argList = (__unsafe_unretained id  *) calloc(1UL, sizeof(id) * arguments.count);
    for (NSInteger i = 0; i < arguments.count; i++) {
        argList[i] = arguments[i];
    }

    NSString* result = [[NSString alloc] initWithFormat:format, *argList] ;//  arguments:(void *) argList];
    free (argList);
    return result;
}
like image 152
Brett Avatar answered Oct 23 '22 12:10

Brett


Cannot find a way to do this obj-c but a swift helper class finally got this working (my whole project is obj-c except this class)

@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)
    }
}

There is some magic going on, to do with how [CVarArgType] doesn't behave like a normal array - but this works in the flexible cross architecture way you expect it to.

like image 1
mcfedr Avatar answered Oct 23 '22 11:10

mcfedr