NSStringWithFormat Swizzled to allow missing format numbered args

Posted by coneybeare on Stack Overflow See other posts from Stack Overflow or by coneybeare
Published on 2010-06-01T00:29:00Z Indexed on 2010/06/01 0:33 UTC
Read the original article Hit count: 447

Based on this SO question asked a few hours ago, I have decided to implement a swizzled method that will allow me to take a formatted NSString as the format arg into stringWithFormat, and have it not break when omitting one of the numbered arg references (%1$@, %2$@)

I have it working, but this is the first copy, and seeing as this method is going to be potentially called hundreds of thousands of times per app run, I need to bounce this off of some experts to see if this method has any red flags, major performance hits, or optimizations

#define NUMARGS(...)  (sizeof((int[]){__VA_ARGS__})/sizeof(int))
@implementation NSString (UAFormatOmissions)
+ (id)uaStringWithFormat:(NSString *)format, ... {  
    if (format != nil) {
        va_list args;
        va_start(args, format);

        // $@ is an ordered variable (%1$@, %2$@...)
        if ([format rangeOfString:@"$@"].location == NSNotFound) {
            //call apples method
            NSString *s = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
            va_end(args);
            return s;
        }

        NSMutableArray *newArgs = (NSMutableArray *)[NSMutableArray arrayWithCapacity:NUMARGS(args)];
        id arg = nil;
        int i = 1;
        while (arg = va_arg(args, id)) {
            NSString *f = (NSString *)[NSString stringWithFormat:@"%%%d\$\@", i];
            i++;
            if ([format rangeOfString:f].location == NSNotFound) continue;
            else [newArgs addObject:arg];
        }
        va_end(args);

        char *newArgList = (char *)malloc(sizeof(id) * [newArgs count]);
        [newArgs getObjects:(id *)newArgList];
        NSString* result = [[[NSString alloc] initWithFormat:format arguments:newArgList] autorelease];
        free(newArgList);
        return result;
    }
    return nil;
}

The basic algorithm is:

  1. search the format string for the %1$@, %2$@ variables by searching for %@
  2. if not found, call the normal stringWithFormat and return
  3. else, loop over the args
  4. if the format has a position variable (%i$@) for position i, add the arg to the new arg array
  5. else, don't add the arg
  6. take the new arg array, convert it back into a va_list, and call initWithFormat:arguments: to get the correct string.

The idea is that I would run all [NSString stringWithFormat:] calls through this method instead.

This might seem unnecessary to many, but click on to the referenced SO question (first line) to see examples of why I need to do this.

Ideas? Thoughts? Better implementations? Better Solutions?

© Stack Overflow or respective owner

Related posts about variable

Related posts about nsstring