在Objective-C块中,BOOL返回类型出现问题

22

我在使用块(Block)中BOOL返回类型时遇到了一个奇怪的问题。定义如下:

typedef BOOL (^BoolBlock)(void);

...这段代码通过测试:

BoolBlock foo = ^{ return YES; };

...但是这个无法编译:

BoolBlock bar = ^{ return YES || NO; };

使用以下错误消息:

不兼容的块指针类型初始化'BoolBlock'(即'BOOL(^) (void)'),使用'type 'int (^) (void)'的表达式

我可以通过使用显式转换来解决此问题,但是不需要这样做吗?是否有更好的解决方案?

3个回答

15

正如Chuck所说,||运算符返回int类型。

BoolBlock bar = ^{ return (BOOL)(YES || NO); };

或者

BoolBlock bar = ^BOOL (void){ return YES || NO; };
BoolBlock bar = ^BOOL (){ return YES || NO; }; // warns in gcc, ok with clang

1
谢谢, 在块定义中声明返回类型有助于解决问题,这比在返回之前进行强制转换更好。Clang 对 ^BOOL () {…} 很满意。 - zoul
1
谢谢!我也遇到了同样的问题,我试图使用... = BOOL^而不是... = ^BOOL - ribeto

11

你可能认为在Ruby和Python等语言中,|| 运算符返回第一个真值的操作数。但在C语言中,如果任一操作数是真值,则返回1;否则返回0。这就是为什么它会认为你返回了一个整数。


3
为什么像 -(BOOL) foo { return YES || NO; } 这样的东西可以编译?换句话说,在块的情况下整数和 BOOL 的区别为什么很重要,而在函数的情况下不重要? - zoul
2
@zoul:该函数具有显式的返回类型,因此int将隐式转换为BOOL。但是您创建的块没有显式的返回类型,因此它从返回语句中推断其返回类型(这是块具有的特殊能力)。您正在返回一个int,因此它推断块返回一个int。仅在完全推断出块的类型之后,编译器才会将该类型与您分配给它的变量的类型进行检查,因此您会得到类型不匹配的错误。 - Chuck

5
正如其他人所说,你之所以出现错误是因为e0 || e1返回一个int,而不管e0e1的类型如何。由于编译器根据return语句推断块返回类型,因此你有一个返回int的块,并且你试图将其赋值给一个块变量,该变量的块返回类型为BOOL
我个人更喜欢这种语法:
BoolBlock bar = ^BOOL { return YES || NO };

为了避免错误,需要明确块返回类型为BOOL。rvalue是一个块文字,被理解为其返回类型为BOOL的块,并且编译器应用通常的C转换。
至于为什么会发生这种情况,这是一个设计决策,尽管似乎没有明确记录。块是一种新的语言特性。编译器设计者已经决定它们应该在块上具有更紧密的语义 - 即,块指针类型的赋值必须具有严格匹配的类型 - 并且无论rvalue是块指针还是块文字,它们都强制执行这些更严格的语义。

由于目前没有ISO/IEC标准覆盖C或C++中的块,编译器设计人员可以自由地做出这些决策。苹果已将块提交给 ISO/IEC JTC1 / SC22 / WG14作为 WG14/N1370 WG14/N1451,如果他们接受它,这种行为(或其某个变体)应该被标准化和记录。

1Clang的源代码确实有一条注释,说明块指针的赋值比函数指针的赋值更严格。

2我个人向他们询问过这个问题。


“^BOOL {}” 是有效的吗?似乎 Blocks 概述Blocks 提案 都没有提到这样的表示法,只有 "^void (void){}", "^(void) {}", 和 "^{}"。 - Kazuki Sakamoto
@Kazuki 这个提案表示:具有空参数列表的闭包可以通过声明为 (void) 或完全省略 (void) 来指定。 - user557219
@Kazuki 谢谢你提供的建议链接! - user557219
@Kazuki 我明白你的意思。我认为当前的措辞是含糊不清的,而且目前编译器是最终的“标准”(尽管这可能被视为一个错误)。 - user557219
谢谢您告诉我!我没有注意到它。 - Kazuki Sakamoto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接