问题
I have a question regarding the best practice for declaring a block as a variable.
Initially I wrote my block variable like this:
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:
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?
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.
回答1:
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 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.
回答2:
Additional to the problem mentioned by dasblinkenlicht I want to ask a rhetoric question:
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:
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.
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.
回答3:
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...
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.
To make it easier, and consequently less error prone, use a typedef, e.g.:
typedef void (^AlertActionHandler)(UIAlertAction * action);
...
AlertActionHandler actionHandler = ^(UIAlertAction * action) { ...
来源:https://stackoverflow.com/questions/42647453/best-practice-to-declare-objective-c-blocks-as-variable