在此代码中
id (^block)(void) = ^(void) {
return nil;
};
我有这个错误
用类型为'void *(^)(void)'的表达式初始化'id (^__strong)(void)'的不兼容块指针类型
我需要显式地将nil
转换为id
类型
id (^block)(void) = ^(void) {
return (id)nil;
};
让编译器高兴。买nil
为什么不是id
型?
对于这个代码
__typeof__(nil) dummy;
dummy = [NSObject new];
将Objective-C指针类型'NSObject *'隐式转换为C指针类型'typeof (((void *)0))'(也称为'void *')需要桥式强制转换
这是说nil
是(void *)0
,但不只是NULL
一样?我认为nil
应该是(id)0
, Nil
应该是(Class)0
?
我使用的是Xcode 4.6.2
编译器:Apple LLVM version 4.2 (clang-425.0.28)(基于LLVM 3.2svn)您正在为块文字使用推断返回类型,但您不必这样做。
消除该错误的正确方法是为块文字提供返回类型:
id (^block)(void) = ^id{
return nil;
};
当一个块省略了它的返回类型时,现在编译器的工作是根据块体中的返回值推断块的类型。但是,不幸的是,CLANG搜索它返回类型的完全衰减形式,这导致了奇怪的不一致。
最容易理解的例子是对数组排序。比较器块的类型是NSComparisonResult(^)(id obj, id obj2)
,但是如果你选择省略NSComparisonResult返回类型,编译器就会发出抱怨:
NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id obj, id obj2) {
if (//...) return NSOrderedAscending;
return NSOrderedDescending;
}];
这个块衰变成一个int(^)(id obj, id obj2)
类型的块,编译器认为这是不兼容的(有争议的是,枚举只是命名为int,所以这应该可以编译)。
以同样的方式,id衰减到objc_object*
,直到它衰减到void*
,或者"任何通用指针"(根据C语言规范)。Void *和id肯定不是同一类型。要解决这个问题,你必须向编译器保证返回的类型是真实的,唯一的方法就是强制类型转换。
LLVM 5.0通过使编译器的推断更加智能来修复这个问题。
我不知道为什么nil是#define nil NULL
在苹果头(这是一个事实,但我不知道为什么它不是(id)0
的原因,因为人们会期望),但我从来没有你在最新版本的Xcode和编译器中提到的错误。
我想我记得遗留编译器的早期版本中有一个错误,它错误地解释了块返回类型等隐式强制转换。
你使用的是哪个编译器?Llvm-gcc 4.2 ?还是苹果LLVM 4.2 ?旧的编译器?检查项目的生成设置。我建议切换到Apple LLVM,并检查是否解决了你的问题。
(事实上,LLVM-GCC仅用于从旧的GCC过渡到新的LLVM,因为Xcode的某些版本现在,但已被弃用,并将在Xcode的下一个版本中删除)