为什么Complie Error只在选择x86_64模拟器时“无法引用块内的数组类型的声明”

时间:2017-09-01 10:34:14

标签: ios objective-c iphone xcode block

我的问题是:为啥仅在使用x86_64编译时会出现此编译错误?以及如何解决?
我的问题是:为什么只有当选择x86_64模拟器编译错误时才会发生?以及如何解决?

环境:Xcode v8.3.2
测试代码:

+ (BOOL)updateSqlByFileName:(NSString *)file key:(NSString *)key, ...
{
    va_list args;
    va_start(args, key);

    __block BOOL isOK = NO;
    [_queue inDatabase:^(FMDatabase *_dataBase)//
    {
        isOK = [_dataBase executeUpdate:sql withVAList:args];
    }];

    va_end(args);
    return isOK;
}
不同的编译方式,编译错误情况
  • 【编译正常√】选择通用iOS设备编译(构建)时:【armv7 + arm64】

    CompileC Test.m normal armv7 objective-c com.apple.compilers.llvm.clang.1_0.compiler
    CompileC Test.m normal arm64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
    
  • 【编译正常√】选择真机:iPhone 4(7.1.2)编译(Build)时:【armv7】

    CompileC Test.m normal armv7 objective-c com.apple.compilers.llvm.clang.1_0.compiler
    
  • 【编译错误×】选择模拟器:iPhone 5s(10.3),iPhone SE(10.3),iPhone 7 Plus(10.3)编译(Build)时:【x86_64】

    CompileC Test.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
    
      

    编译错误
      无法在块

    中引用带有数组类型的声明
  • 【编译正常√】选择模拟器:iPhone 5(10.3)编译(Build)时:【i386】

    CompileC Test.m normal i386 objective-c com.apple.compilers.llvm.clang.1_0.compiler
    

2 个答案:

答案 0 :(得分:2)

如果块捕获非__block变量,则在创建块并将其存储在块中时,会生成变量的副本。无法在C中分配数组,这可能就是为什么块的设计者不允许在块中捕获数组类型的变量。

C标准未指定哪种类型va_list;它是特定于实现的。它可以实现为数组类型,指针类型,结构类型等等,并且这可以在同一编译器上的体系结构之间有所不同。可能他们在x86_64中将其实现为数组类型,在其他3种体系结构中实现为某些非数组类型。这没什么不寻常的。您无法假设va_list类型是什么类型。

在您定义另一个函数并将va_list传递给它的答案中,这恰好适用于va_list是数组类型的情况,因为C会自动调整“数组”类型的任何参数T“to”指向T“的指针,因此当args是数组类型时,方法+foo:key:withVAList:中的参数va_list实际上具有指针类型(与{{1}不同的类型在args方法中),指针变量可以被捕获到一个块中。

另一种解决方案是获取+foo:key:的地址,获取va_list并将其放入块中使用的变量中。无论va_list *是什么,这都保证是指针类型,并且可以在块中捕获。当您需要使用块内的实际va_list时,您可以取消引用指针。例如,像这样:

va_list

答案 1 :(得分:-2)

经过尝试【有效的】解决方案(代码):
+ (BOOL)foo:(NSString *)file key:(NSString *)key, ...
{
    va_list args;
    va_start(args, key);

    BOOL isOK = [self foo:file key:key withVAList:args];

    va_end(args);
    return isOK;
}

+ (BOOL)foo:(NSString *)file key:(NSString *)key withVAList:(va_list)args
{
    __block BOOL isOK = NO;
    [_queue inDatabase:^(FMDatabase *_dataBase){
        isOK = [_dataBase executeUpdate:sql withVAList:args];
    }];
    return isOK;
}
参考代码(灵感来源):

FMDB类库里的executeQuery函数

- (FMResultSet *)executeQuery:(NSString*)sql, ... {
    va_list args;
    va_start(args, sql);

    id result = [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args];

    va_end(args);
    return result;
}
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
    //xxx
}