In this post, I start with the simplest C declarator and build in complexity until we get to Objective-C blocks syntax. It took me a while to get block syntax but once you understand how it is organized and where it comes from, there is no looking in Google every time you need to declare a block anymore.
If you want to be able to write block declarations from the top of your head, read-on!
I strongly advise against reading this post in an RSS reader or read later service. It does heavy use of colors to explain things and these colors would not appear there.
Variables in C (and by extension Objective-C) are declared using declarators.
A declarator has 2 roles:
- Specify the type of the variable (what the compiler should expect to find in that memory space)
- Give that variable a name to make it available to the appropriate scope.
Let’s start with the most basic declarator:
This is most likely the first line of C code you have ever written.
int is a basic type and
a is the variable name or identifier1.
To read a declarator, you start from the identifier, then go right until you can’t and then you start over from the left of the variable2 (we’ll explain why in the next section).
Here there is nothing to the right of our variable so it’s straightforward:
a is an
A declaration can only have 1 basic type and it’s the left most word of the declarator.
Declarators can modify basic types using modifiers to create derived types. The 4 modifiers (3 from ANSI-C and one from Apple’s proposed extension) are
The 3 ANSI-C modifiers
The pointer modifier
The basic type is still int and the name of the variable
a. But the pointer modifier
* comes to tell us that
a is a pointer to an
int instead of an
* modifier is always to the left of the modified variable.
The array modifier
Here we see that the array modifier
 comes to tell us that
a is now an array of
ints instead of a simple
int. This can be completed by the dimension of the array e.g.
 modifier is always to the right of the modified variable.
The function modifier
The function modifier
() comes to tell us that
f is a function that returns an
int. This modifier can also specify the arguments that the function takes e.g.
int f(long); is a function that takes a long as an argument and returns an int.
() modifier is always to the right of the modified variable.
Pointers and arrays
Modifiers can be composed to create more complex variable types. Similarly to how arithmetic operations are ordered by precedence (* and / are executed before + and –), modifiers are too.
() have higher precedence over
Since the 2 modifiers with higher precedence are written to the right of the variable, when reading complex declarator, you always start from the identifier and go right as long as possible then go left when you reach either the end of the declarator or a closing parenthesis.
or as you can write it by adding parentheses to improve readability
is an array of pointers to an
But you might ask, what if I want to have a pointer to an array of ints? Well since
* has lower precedence than
, you need to use parenthesis to force that precedence.
a is a pointer to an array of
Array and functions
You cannot have an array of functions and a function cannot return an array or a function3. A function can however take an array as an argument.
int f(int );
Here f is a function that takes an array of 10
ints as an argument and returns an
Pointers and functions
int *f(); int *(f());
In both cases,
f is a function that returns a pointer to an
What if you want a pointer to a function? Parentheses!
f is a pointer to a function that returns an
The block (or closure) pointer modifier
Apple introduced a 4th modifier in its proposed extension of the ANSI-C standard:
This modifier is called the block pointer modifier (or closure modifier as they were originally called). Blocks are very similar to pointers for functions. You declare a block the same way you would declare a pointer to a function.
The block pointer modifier can only be applied to a function (you cannot write
int ^a; as this is not defined).
This is the reason why
int ^b() is illegal and will cause a compiler error: If you read this declarator using the precedence rules, b would be a function that returns a block pointer to an int. There is no such thing and this is why when declaring a block you need to always put the caret and the identifier in parentheses.
b is a block pointer to a function that returns an
int or as abbreviated a block that returns an
You can of course specify the arguments that the block takes:
is a block that takes a long as an argument returns a
This is where the syntax for block declarations comes from.
Now you already know that there are other syntaxes that you need to remember in order to use blocks: the one used to define a block literal, and the one to pass the block to an Objective-C method.
A declarator is made up of 2 things: an abstract declarator in which you insert the identifier.
Abstract declarators are used in 3 cases in standard C:
In casts: in
int *a; long *b = (long *) a;,
(long *)is an abstract declarator for a pointer to a
As arguments of sizeof():
When declaring argument types for a function:
int f(long *);
Objective-C uses abstract declarators in one more place: when declaring arguments or return values for methods.
long ** and
int * are abstract declarators.
So in order to use blocks as arguments or return values for Objective-C methods, we need to find the abstract declarator for these blocks. This is achieved by taking the declarator and removing the identifier.
int (^b)() becomes
int (^)() and
int (^b)(long) becomes
While you don’t have to name your block’s arguments in these abstract declarators, it is a good idea to do it. It will give a good hint as to what the block expects as argument and Xcode autocomplete will make your life easier when using that method.
When you write
int a = 2;,
int a is a declarator and
2 is a literal for an int.
^ is also used as a unary operator to transform a function implementation into a block4. You don’t need to specify the return type for the block (it is inferred from the return statement in that block) but you can.
Since this is the implementation of that block, you need to name your arguments here.
So for the block
int (^block)(long, long);, a block literal would be:
As convoluted as it may seem, blocks syntax in Objective-C is built upon standard C syntax. A block in Objective-C is nothing more than a pointer to a function that captures its scope. Once you understand that and practice writing and reading a few blocks declaration, you’ll find it much easier to apprehend.