我的问题是:为啥仅在使用 x86_64 编译时会出现此编译错误?以及如何解决?
My question is: why only when select x86_64 simulator compile error occurs? And how to solve?
环境: 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;
}
不同的编译方式,编译错误情况:
【编译正常√】选择 Generic iOS Device 编译(Build)时:【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
Complie Error:
Cannot refer to declaration with an array type inside block
【编译正常√】选择模拟器: iPhone 5(10.3)编译(Build)时:【i386】
CompileC Test.m normal i386 objective-c com.apple.compilers.llvm.clang.1_0.compiler
If a block captures a non-__block
variable, a copy of the variable is made when the block is created and stored within the block. Arrays cannot be assigned in C, and that may be why the designers of blocks disallowed capturing variables of array type in a block.
What kind of type va_list
is is not specified by the C standard; it is implementation-specific. It could be implemented as an array type, a pointer type, a struct type, or whatever, and this can be different across architectures on the same compiler. Probably they implemented it as an array type in x86_64 and as some non-array type on the other 3 architectures. There is nothing unusual about this. You cannot make assumptions about what kind of type va_list
is.
In your answer where you defined another function and passed the va_list
to it, that happens to work in the case where va_list
is an array type, because C automatically adjusts any parameters of type "array of T" to "pointer to T", so the parameter args
in the method +foo:key:withVAList:
actually has a pointer type when va_list
is an array type (different type than the args
in the +foo:key:
method), and pointer variables can be captured into a block fine.
An alternative solution would be to take the address of the va_list
, getting a va_list *
and putting that into a variable that is used in the block. This is guaranteed to be a pointer type no matter what va_list
is, and can be captured in blocks fine. When you need to use the actual va_list
inside the block you can dereference the pointer. For example, something like this:
+ (BOOL)updateSqlByFileName:(NSString *)file key:(NSString *)key, ...
{
va_list args;
va_start(args, key);
va_list *ptrToArgs = &args;
__block BOOL isOK = NO;
[_queue inDatabase:^(FMDatabase *_dataBase)
{
isOK = [_dataBase executeUpdate:sql withVAList:*ptrToArgs];
}];
va_end(args);
return isOK;
}