From C Declarators to Objective-C Blocks Syntax

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.

Declarators

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:
int a;
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 int.
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 *, [], () and ^.

The 3 ANSI-C modifiers

The pointer modifier *
int *a;
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 int.
The * modifier is always to the left of the modified variable.
The array modifier []
int a[];
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. int a[10];.
The [] modifier is always to the right of the modified variable.
The function modifier ()
int f();
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.
The () modifier is always to the right of the modified variable.

Composing modifiers

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. [] and () have higher precedence over * (and ^). 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.
int *a[];
or as you can write it by adding parentheses to improve readability
int *(a[]);
is an array of pointers to an int.
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.
int (*a)[];
Here, a is a pointer to an array of ints.
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 [10]);
Here f is a function that takes an array of 10 ints as an argument and returns an int.
Pointers and functions
int *f();
int *(f());
In both cases, f is a function that returns a pointer to an int.
What if you want a pointer to a function? Parentheses!
int (*f)()
f is a pointer to a function that returns an int.

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.
int (^b)()
b is a block pointer to a function that returns an int or as abbreviated a block that returns an int.
You can of course specify the arguments that the block takes:
int (^b)(long)
is a block that takes a long as an argument returns a int.
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.

Abstract declarator

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 long.
  • As arguments of sizeof(): malloc(sizeof(long *));
  • 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 **)methodWithArgument:(int *)a;
Here both 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 int (^)(long).
example:
- (void)methodWithArgument:(int(^)())block;
- (void)anotherMethodWithArgument:(void(^)(long arg1))block;
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.

Block literals

When you write int a = 2;, int a is a declarator and 2 is a literal for an int.
The caret ^ 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:
block = ^(long a, long b) {
  int c = a + b;
  return c;
}

Conclusion

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.

  1. From ANSI-C draft page 35: “An identifier can denote an object; a function; a tag or a member of a structure, union, or enumeration; a typedef name; a label name; a macro name; or a macro parameter”
  2. Oddly enough, Microsoft, of all companies, has a good page explaining the interpretation of complex declarators.
  3. From ANSI-C draft page 133: “A function declarator shall not specify a return type that is a function type or an array type.
  4. From Clang documentation: “A Block literal expression produces a reference to a Block. It is introduced by the use of the ^ token as a unary operator.
Previous
Next Post »