In my attempts to understand what I can and can't do with a va_list in (Objective-)C, I came across this little puzzle. I was hoping to create a category on NSString
that would simplify the stringWithFormat:
message a bit in some cases, just for the fun of it. What I was aiming for was being able to use the implementation like this:
[@"My %@ format %@!" formattedWith:@"super", @"rocks"];
Hoping to end up with a string saying "My super format rocks!
". My (incorrect) method implementation looks like this:
- (NSString *)formattedWith:(NSString *)arguments, ...
{
va_list list;
va_start(list, arguments);
NSString *formatted = [[[NSString alloc] initWithFormat:self arguments:list] autorelease];
va_end(list);
return formatted;
}
Now the problem is that as soon 开发者_运维技巧as va_start()
is called, the va_list is 'shortened' (for lack of a better word) and only contains the rest of the arguments (in the case of the example only @"rocks"
remains, plus the calling object which I don't care about). What's passed onto the initWithFormat:
message therefore renders the wrong kind of result.
On to the question. Are there ways to modify the va_list before I pass it to the initWithFormat:
message, so I can somehow shift the first argument back onto the list?
I'm not looking for an iterative process where I loop through the va_list myself, I'm looking to understand the limits of va_list as a whole. Thanks!
The va_list
can not be modified safely. The va_start
argument also requires an argument to start the list which is excluded. The ways to work around this are to either pass an additional useless argument or use variadic macros.
//Method declaration
-(NSString*)formattedWith:(NSString*)ignored,...;
//Opt 1 (pass anything for first value)
[@"My %@ format %@!" formattedWith:nil, @"super", @"rocks"];
//Opt 2 (create a macro for the arguments)
#define VA_OPT(...) nil, ##__VA_ARGS__ //VARIADIC OPTIONAL
[@"My %@ format %@!" formattedWith:VA_OPT(@"super", @"rocks"];
//Opt 3 (just create a macro for the whole string format)
//Obviously you should just use the NSString method directly before resorting to this
#define FORMATTED_NSSTRING(str, ...) [NSString stringWithFormat:str, ##__VA_ARGS__]
FORMATTED_NSSTRING(@"My %@ format %@!", @"super", @"rocks")
精彩评论