Does Objective-C Block Syntax Make F'n Sense?
by Lou Franco
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 int *
.
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:
-(void)doBlock:(typename)block_param
Where typename
is 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.
So, int (^block_variable)(int, int)
becomes:
typedef int (^block_t)(int, int)
And then your method can be declared as:
-(void)doBlock:(block_t)block_param
So, block syntax might make a little sense, as long as you remember:
- It's kind of like C pointers to functions
- So, replace the name with the pointer operator
- Oh yeah, and it needs to be in parens because of C's associativity rules
- Except, use
^
instead of*
- And, if you need a name (for a variable or typedef), put it in the parens too (because C syntax)
- But, if it's a method parameter, then just follow Objective-C style (extra parens and name outside)
And if you don't get it on the first try, just look it the F up.