Filed under: iOS
You know the feeling. You're refactoring that old ViewController that’s still in Objective-C, and you get to that part where you need to extract an async call to a new class. You’ve gotten so used to closures in Swift that you're using them instead of delegates, even in Objective-C. And you say to yourself, “I know—I’ll use a block.”
Now you have two problems.
But no worries, because every Objective-C developer knows to go to F'n Block Syntax to look up block syntax, because, well, block syntax is F'd. Even after you’ve looked it up, it doesn’t stick. But I remember at one time thinking it kind of made sense, so I tried to reconstruct my understanding.
Before I was doing iOS, I was mostly a C/C++ developer, and even though the syntax for types can be complex (especially with pointers), I did find it to be consistent. C pointer-to-function syntax was probably the basis of Objective-C block syntax, so it’s worth taking a look.
To start, a C function declaration looks like this:
int add_ints(int x, int y)
This reads as: “add_ints is a function that takes two ints and returns an int.” The type of the "pointer to this function", if you wanted to assign a variable to it, is:
int (*)(int x, int y)
Essentially, you just replace the name with a
*, but it needs to be in parentheses because of operator precedence and associativity. In C, the
* would associate with the
int and change the return type to
The weird part comes when you want to declare a variable with this type. To make it as close as possible to a function declaration, we want the name between the return type and the parameters. And it turns out, we want the
* to associate with that name, so we put it inside the parens, like so:
int (*func_ptr_variable)(int, int) = &add_ints;
The only thing that Objective-C did to this syntax is swap a
^ for the
* to make a special operator to use for blocks (and break the association to pointers).
So, the equivalent variable declaration is:
int (^block_variable)(int, int)
So, one way to remember this is to base the declaration on C style functions (like
return name(params)) and to think of the
^ as a replacement for C’s
* for blocks, and then to use parentheses to associate it with the "name".
When you want to pass a block to a method, you just need to remember that Objective-C method syntax always puts parentheses around the parameter type and the name outside the close paren, so it always looks like:
return (^)(params), so
-(void)doBlock:(int (^)(int, int))block_param
If it starts getting more complicated than this, you probably want a typedef, and that is made exactly the same way as a variable, but with the
typedef keyword before it.
int (^block_variable)(int, int) becomes:
typedef int (^block_t)(int, int)
And then your method can be declared as:
So, block syntax might make a little sense, as long as you remember:
And if you don't get it on the first try, just look it the F up.