Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

va_args() causing EXC_BAD_ACCESS

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.

like image 735
Jeff Avatar asked Aug 04 '14 20:08

Jeff


1 Answers

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.

like image 81
rmaddy Avatar answered Nov 11 '22 01:11

rmaddy