8

我正在使用基于块的 API,并偶然发现了一个场景,即我传入的块参数的签名与方法所期望的 typedef'd 参数不匹配。令我惊讶的是,编译器似乎并不关心这一点,应用程序也没有崩溃。这是预期的行为吗?例子:

typedef void(^MyBlock)();
typedef void(^MyBlockWithParam)(id param);

- (void)doWork {
    MyBlockWithParam block1 = ^(id param) {
        NSLog(@"block1: %@", param);
    };

    MyBlock block2 = ^{
        NSLog(@"block2");
    };

    [self loadData:block1];
    [self loadData:block2];
}

- (void)loadData:(MyBlockWithParam)block {
    block(@"foo");
}
4

3 回答 3

9

提供一个空参数规范,如

typedef void(^MyBlock)();

表示“未指定”的论点。所以这两种类型是兼容的。将第一个声明更改为

typedef void(^MyBlock)(void);

指定该块不接受任何参数,您将收到错误消息。

K&R C 指定空参数列表表示“未指定”。C 块规范说这对于块类型声明不正确(参见http://clang.llvm.org/docs/BlockLanguageSpec.html#block-variable-declarations但是:GCC 和 Clang 都将 K&R 行为作为语言扩展。

于 2013-08-09T17:29:18.973 回答
4

来自 Clang 块规范:

支持可变...参数。[variadic.c]不带参数的块必须在参数列表[voidarg.c] 中指定 void。正如 K&R 提供的那样,空参数列表并不表示未指定的参数列表。注意:为了方便起见,gcc 和 clang 都支持 K&R 样式。

基本上,这是 C 语法的一个古老的怪癖。在过去,C 函数声明语法相当不同,空括号表示函数可以传递任意数量的参数。为了向后兼容,编译器通常允许使用旧式函数声明语法。出于某种原因,Apple 决定同时拒绝块标准中的这种语法,而实际上允许它与 GCC 和 Clang 中的块一起使用。

所以,长话短说:要声明一个块不带参数,您需要将其显式键入为void(^MyBlock)(void)

于 2013-08-09T17:24:11.800 回答
3

这是C的事情。具有空参数列表的函数或块原型意味着“函数(或块)采用您喜欢的任何参数”。如果你想表达块不应该有参数,你需要像这样明确地说:

typedef void(^MyBlock)(void)

这在很大程度上是 ANSI 之前没有函数原型并且所有函数声明(而不是定义)看起来像这样的历史:

some_type function();
于 2013-08-09T17:23:15.653 回答