va_args()导致EXC_BAD_ACCESS

Jef*_*eff 5 sqlite objective-c variadic-functions fmdb ios

我在使用va_args(iOS 7,Xcode 5.1.1,ARC on)时获得了EXC_BAD_ACCESS:

    // ...
    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
}
Run Code Online (Sandbox Code Playgroud)

如果我说"休息" 在循环结束时(因为我只有一个参数),或者添加"nil"作为最后一个参数,没有崩溃,但我认为我不应该添加"nil".我怀疑是ARC问题,但我正在使用__unsafe_unretained,正如其他地方所建议的那样.(有没有办法可以将"nil"推入args?)

是什么导致第二次通过循环失败?


编辑8月6日:我的解决方案:

当他提到"格式说明符的数量"时,maddy接受的解决方案将我推向了正确的方向.我的格式参数有'?' 每个参数的占位符,所以我只计算那些.所以,为了记录:

- (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;
}
Run Code Online (Sandbox Code Playgroud)

这是一个双重包装.我的例程是FMDB的包装器,它本身就是SQLite的包装器.

rma*_*ddy 7

你需要nil或者用其他方式知道要抓取多少个参数.va_list没有神奇的方法知道何时停止.

stringWithFormat:不需要的东西nil因为它根据格式说明符的数量确定了参数的数量(这就是为什么它们需要匹配或者你的代码繁荣的原因).注意类似的方法如何NSDictionary dictionaryWithObjectsAndKeys:需要nil终结符或UIAlertView initWithTitle...需要参数的nil终止符otherButtonTitles.

你可以做的是使用以下NSString方法:

- (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;
}
Run Code Online (Sandbox Code Playgroud)

当然,此解决方案假定您希望从format方法构建字符串和变量参数.

如果确实需要填充数组,那么nil在调用sqlIntQuery方法时需要传递终结符.