Does Objective-C Block Syntax Make F'n Sense?

by Lou Franco

Filed under: iOS

Photo by Martin Sanchez on Unsplash

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.