I'm getting a EXC_BAD_ACCESS when using va_args (iOS 7, Xcode 5.1.1, ARC on):
// ...
int val = sqlIntQuery(@"format_string", @"arg1"); // <-- does not work
int val = sqlIntQuery(@"format_string", @"arg1", nil); // <-- this works
// ...
- (int)sqlIntQuery:(NSString *)format, ...
{
va_list args;
va_start(args,format);
__unsafe_unretained id eachObject;
NSMutableArray *arguments = [NSMutableArray array];
while ( (eachObject = va_arg(args, id)) != nil ) { // <-- crash on 2nd loop
[arguments addObject:eachObject];
}
va_end(args);
// ... process 'arguments'
return 5; // return a computed intValue
}
If I put a "break;" at the end of the loop (because I only have one argument), or add "nil" as a last argument, there's no crash, but I don't think I should have to add the "nil". I suspect an ARC issue, but I'm using __unsafe_unretained, as suggested elsewhere on SO. (Is there a way I can push "nil" into to args?)
What's causing the failure on the second time through the loop?
EDIT Aug 6: My Solution:
The accepted solution by maddy pushed me in the right direction when he mentioned "the number of format specifiers." My format argument has a '?' placeholder for each argument, so I just count those. So, for the record:
- (int)sqlIntQuery:(NSString *)format, ...
{
int numberOfArgs = [format componentsSeparatedByString:@"?"].count - 1; // <<-- this solved my problem
va_list args;
va_start(args,format);
NSMutableArray *arguments = [NSMutableArray array];
while ( numberOfArgs-- ) {
id eachObject = va_arg(args, id);
[arguments addObject:eachObject];
}
va_end(args);
FMResultSet *rs = [db executeQuery:format withArgumentsInArray:arguments];
[rs next];
int ret = [rs intForColumnIndex:0];
[rs close];
return ret;
}
It's a double-wrapper. My routine is a wrapper around FMDB, which is itself a wrapper for SQLite.
You need the nil
or some other way to know how many arguments to grab. va_list
has no magic way to know when to stop.
Things like stringWithFormat:
don't need a nil
because it determines the number of arguments based on the number of format specifiers (which is why they need to match or your code goes boom). Note how methods like NSDictionary dictionaryWithObjectsAndKeys:
requires a nil
terminator or UIAlertView initWithTitle...
needs the nil
terminator for the otherButtonTitles
parameter.
What you could do is make use of the following NSString
method:
- (int)sqlIntQuery:(NSString *)format, ... {
va_list args;
va_start(args, format);
NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
// do whatever
va_end(args);
return 5;
}
Of course this solutions assumes you wish to build a string from format
and the variable arguments to your method.
If you really do need to populate an array then you will need to pass a nil
terminator when you call your sqlIntQuery
method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With