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 arrayWithCapacity:NUMARGS(args)];
id arg = nil;
int i = 1;
while (arg = va_arg(args, id)) {
NSString *f = [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:
- search the format string for the
%1$@
,%2$@
variables by searching for%@
- if not found, call the normal stringWithFormat and return
- else, loop over the args
- if the format has a position variable (
%i$@
) for position i, add the arg to the new arg array - else, don't add the arg
- take the new arg array, convert it back into a
va_list
, and callinitWithFormat: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?