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

时间:2023-01-15 14:49:41

I have a question regarding the best practice for declaring a block as a variable.

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

Initially I wrote my block variable like this:

一开始我写的block变量是这样的:

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

To be later used like so:

以后要这样使用:

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

But when I came across Apple's Working With Blocks guide, I saw I could rewrite it like so:

但当我遇到苹果公司的block guide时,我发现我可以这样重写:

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

Is this the 'correct' way to declare it? That is in my opinion not as readable, but I don't have a lot of experience with Objective-C. So what is the best practice for declaring a block as a variable?

这是申报的“正确”方式吗?这在我看来不太容易读懂,但我对Objective-C没有太多经验。那么,将块声明为变量的最佳实践是什么?


Edit: Alright, thanks all for the clarification! Defining a typedef as shown by amin-negm-awad and others seems like a good alternative approach as well.

编辑:好的,谢谢大家的澄清!用amin-negm-awad和其他方法定义类型定义似乎也是一种不错的替代方法。

3 个解决方案

#1


2  

There is no one-fits-all answer here: when you declare your block variable as id you no longer have compile-time information associated with your block, so calling it manually becomes problematic:

这里没有一劳永逸的答案:当您将块变量声明为id时,您将不再拥有与块相关的编译时信息,因此手动调用它将成为问题:

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

if you want to make a direct call to the block from your code, you need to cast it back to a block.

如果想从代码中直接调用块,需要将它转换回块。

On the other hand, if you declare a block variable only so that you could pass it to a function that takes a block as a parameter, using id provides a more readable approach.

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

#2


3  

Additional to the problem mentioned by dasblinkenlicht I want to ask a rhetoric question:

除了dasblinkenlicht提到的问题之外,我想问一个修辞问题:

Likely you know that you can substitute this code …:

你可能知道你可以用这段代码。

NSString *string = @"All about types";

… with this code:

…这段代码:

id string = @"All about types";

Would you do? I'm sure, you don't.

你会做什么?我相信,你不会。

So why should one change the "typed" version of the var into an id version? The only reason is, that the syntax of block types is unhandy and not easy to read (and not easy to write). I always define a concrete type to get rid of the unhandy syntax:

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

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

And then:

然后:

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

To make that clear: id is great to use the dynamic nature of Objective-C's message passing. But block execution is neither late bound. Nor the parameters of the block can change its number or type, so there is nothing to dynamically bind. It is a simple call with fixed numbers of arguments, fixed typed. Therefore the usage of id is possible as a side-effect of the block's object nature. But it is not an usage, which is intended.

要说明这一点:id非常适合使用Objective-C的消息传递的动态特性。但是块执行并不是延迟的。块的参数也不能改变它的数量或类型,因此没有什么可以动态绑定的。它是一个具有固定数量的参数、固定类型的简单调用。因此,id的使用可能是block的对象属性的副作用。但这并不是一种意图。

BTW: If you use a concrete type in a parameter list, Xcode can autocomplete the syntax of the argument. With id this is not possible. Obviously.

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

#3


1  

If you use id in this context the compiler will not check that the type of the block you declare matches the type of the block the method expects. If you accidentally get the block wrong nasty, hard to debug, things will probably happen when the method tries to use the block...

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

So if you never make mistakes go with id, but if like me you do provide the correct type so the compiler can help you out when you do.

所以,如果你从来没有犯过错误,就使用id,但是如果你像我一样,你提供了正确的类型,这样编译器可以在你犯错误的时候帮助你。

To make it easier, and consequently less error prone, use a typedef, e.g.:

为了使它更容易,也因此更不容易出错,使用类型定义,例如:

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

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

#1


2  

There is no one-fits-all answer here: when you declare your block variable as id you no longer have compile-time information associated with your block, so calling it manually becomes problematic:

这里没有一劳永逸的答案:当您将块变量声明为id时,您将不再拥有与块相关的编译时信息,因此手动调用它将成为问题:

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

if you want to make a direct call to the block from your code, you need to cast it back to a block.

如果想从代码中直接调用块,需要将它转换回块。

On the other hand, if you declare a block variable only so that you could pass it to a function that takes a block as a parameter, using id provides a more readable approach.

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

#2


3  

Additional to the problem mentioned by dasblinkenlicht I want to ask a rhetoric question:

除了dasblinkenlicht提到的问题之外,我想问一个修辞问题:

Likely you know that you can substitute this code …:

你可能知道你可以用这段代码。

NSString *string = @"All about types";

… with this code:

…这段代码:

id string = @"All about types";

Would you do? I'm sure, you don't.

你会做什么?我相信,你不会。

So why should one change the "typed" version of the var into an id version? The only reason is, that the syntax of block types is unhandy and not easy to read (and not easy to write). I always define a concrete type to get rid of the unhandy syntax:

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

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

And then:

然后:

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

To make that clear: id is great to use the dynamic nature of Objective-C's message passing. But block execution is neither late bound. Nor the parameters of the block can change its number or type, so there is nothing to dynamically bind. It is a simple call with fixed numbers of arguments, fixed typed. Therefore the usage of id is possible as a side-effect of the block's object nature. But it is not an usage, which is intended.

要说明这一点:id非常适合使用Objective-C的消息传递的动态特性。但是块执行并不是延迟的。块的参数也不能改变它的数量或类型,因此没有什么可以动态绑定的。它是一个具有固定数量的参数、固定类型的简单调用。因此,id的使用可能是block的对象属性的副作用。但这并不是一种意图。

BTW: If you use a concrete type in a parameter list, Xcode can autocomplete the syntax of the argument. With id this is not possible. Obviously.

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

#3


1  

If you use id in this context the compiler will not check that the type of the block you declare matches the type of the block the method expects. If you accidentally get the block wrong nasty, hard to debug, things will probably happen when the method tries to use the block...

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

So if you never make mistakes go with id, but if like me you do provide the correct type so the compiler can help you out when you do.

所以,如果你从来没有犯过错误,就使用id,但是如果你像我一样,你提供了正确的类型,这样编译器可以在你犯错误的时候帮助你。

To make it easier, and consequently less error prone, use a typedef, e.g.:

为了使它更容易,也因此更不容易出错,使用类型定义,例如:

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

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