为什么`nil`的类型不是`id`而是`void *`

时间:2013-06-15 10:40:23

标签: objective-c objective-c-blocks null

在此代码中

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)0Nil应该是(Class)0

我正在使用Xcode 4.6.2

编译器:Apple LLVM版本4.2(clang-425.0.28)(基于LLVM 3.2svn)

3 个答案:

答案 0 :(得分:3)

当一个块省略了它的返回类型时,它现在成为编译器的工作,根据其体内的返回值推断块的类型。但是,不幸的是,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通过使编译器的推理更加智能化来解决这个问题。

答案 1 :(得分:3)

您正在为块文字使用推断的返回类型,但您没有。

删除该错误的正确方法是为块文字提供返回类型:

id (^block)(void) = ^id{
    return nil;
};

答案 2 :(得分:0)

我不知道为什么nil在Apple标题中是#define nil NULL(这是事实,但我不知道为什么它不像人们期望的那样(id)0),但我从来没有您在最新版本的Xcode和编译器中提到的错误。

我想我记得早期版本的遗留编译器中的一个错误,它错误解释了类似于块返回类型的隐式转换。

您使用的是哪种编译器? LLVM-GCC 4.2?还是Apple LLVM 4.2?一个旧的编译器? 检查项目的构建设置。我建议切换到Apple LLVM,并检查是否能解决您的问题

(事实上,LLVM-GCC仅用于从旧GCC到新LLVM的转换,因为现在有一些版本的Xcode,但已被弃用,将在下一版本的Xcode中删除)