将 Objective-C 块声明为变量的最佳实践

Best practice to declare Objective-C blocks as variable

我有一个关于将 块声明为变量的最佳实践的问题

最初我这样写我的块变量:

id actionHandler = ^(UIAlertAction * action) {
    // Handling code
};

以后像这样使用:

UIAlertAction *action = [UIAlertAction actionWithTitle:@"Title"
                                                 style:UIAlertActionStyleDefault
                                               handler:actionHandler];

但是当我遇到 Apple's Working With Blocks 指南时,我发现我可以这样重写它:

void (^actionHandler)(UIAlertAction * action) = ^(UIAlertAction * action) {
    // Handling code
};

这是'correct'的申报方式吗?在我看来,这不是可读的,但我对 Objective-C 没有太多经验。那么将块声明为变量的最佳做法是什么?


编辑: 好的,谢谢大家的澄清! amin-negm-awad 和其他人将 typedef 定义为 似乎也是一个很好的替代方法。

这里没有万能的答案:当您将块变量声明为 id 时,您不再拥有与您的块关联的编译时信息,因此手动调用它会出现问题:

id myHandler = ^(NSString *str) {
    NSLog(@"%@", str);
};
// Error: Called object type id is not a function or function pointer
myHandler(@"Hello");

如果您想从您的代码中直接调用该块,您需要将其转换回一个块。

另一方面,如果您只声明一个块变量以便将其传递给将块作为参数的函数,则使用 id 提供了一种更具可读性的方法。

除了dasblinkenlicht提到的问题我想问一个反问:

您可能知道可以替换此代码……:

NSString *string = @"All about types";

... 使用此代码:

id string = @"All about types";

你愿意吗?我敢肯定,你不知道。

那么为什么要将 var 的 "typed" 版本更改为 id 版本呢?唯一的原因是,块类型的语法不方便且不易阅读(也不易编写)。我总是定义一个具体的类型来摆脱不方便的语法:

typedef void (^ActionHandlerType)(UIAlertAction * action);

然后:

ActionHandlerType actionHandler = ^(UIAlertAction * action) {
  // Handling code
};

明确一点:id 非常适合使用 Objective-C 消息传递的动态特性。但是块执行既不是后期约束。块的参数也不能改变它的数量或类型,所以没有什么可以动态绑定的。这是一个简单的调用,具有固定数量的参数,固定类型。因此,id 的使用可能是块对象性质的副作用。但这不是一种用法,这是有意的。

顺便说一句:如果您在参数列表中使用具体类型,Xcode 可以自动完成参数的语法。使用 id 这是不可能的。显然。

如果您在此上下文中使用 id,编译器将不会检查您声明的块类型是否与方法期望的块类型匹配。如果你不小心把块弄错了,很难调试,当方法试图使用块时,事情可能会发生......

因此,如果您从不犯错,请选择 id,但如果您像我一样提供了正确的类型,那么编译器可以在您犯错时帮助您。

为了更简单,从而更不容易出错,请使用 typedef,例如:

typedef void (^AlertActionHandler)(UIAlertAction * action);
...

AlertActionHandler actionHandler = ^(UIAlertAction * action) { ...