为什么' nil '类型不是' id '而是' void * ' ?



在此代码中

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的下一个版本中删除)

相关内容

最新更新