Objective-C Blocks

Blocks are commonly used for asynchronous programming in Obective-C. While it is easy to start using them, understanding their nuances is more complicated. This post will explain why and how blocks are typically used!

Block Basics

Blocks are Objective-C’s anonymous functions. They let you pass statements between objects as you would data!

In addition, blocks are implemented as closures, which means they capture the surrounding state. You need to make sure your block is written with this in mind!

Building Blocks

Blocks are used very similiarly to functions. You can declare the block variable, define the block, and then call the block as if it were a function:

main.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Declare the block variable
double (^milesPerHour)(double miles, double hours);
// Create and assign the block
milesPerHour = ^double(double miles, double hours) {
return miles / hours;
};
// Call the block
double mph = milesPerHour(120, 2.0);
NSLog(@"A car that drives 120 miles in 2.0 hours is "
@"travelling at %.2f mph.", mph);
}
return 0;
}

The caret (^) is used to mark the milesPerHour variable as a block. Like a function declaration, you need to include the return type and parameter types so the compiler can enforce type safety. The ^ behaves in a similar manner to the asterisk before a pointer (e.g., int *aPointer) in that it is only required when declaring the block, after which you can use it like a normal variable.

The block itself is essentially a function definition—without the function name. The ^double(double rate, double time) signature begins a block literal that returns a double and has two parameters that are also doubles (the return type can be omitted if desired). Arbitrary statements can go inside the curly braces ({}), just like a normal function.

After assigning the block literal to the milesPerHour variable, we can call that variable as though it were a function.

So far, it might seem like blocks are just a complicated way of defining functions. But, the fact that they’re implemented as closures opens the door to exciting new programming opportunities.

Closures

Inside of a block, you have access to same data as in a normal function: local variables, parameters passed to the block, and global variables/functions. But, blocks are implemented as closures, which means that you also have access to non-local variables. Non-local variables are variables defined in the block’s enclosing lexical scope, but outside the block itself. For example, getFullCarName can reference the make variable defined before the block:

1
2
3
4
5
NSString *make = @"Honda";
NSString *(^getFullCarName)(NSString *) = ^(NSString *model) {
return [make stringByAppendingFormat:@" %@", model];
};
NSLog(@"%@", getFullCarName(@"Accord")); // Honda Accord

Non-local variables are copied and stored with the block as const variables, which means they are read-only. Trying to assign a new value to the make variable from inside the block will throw a compiler error.

The fact that non-local variables are copied as constants means that a block doesn’t just have access to non-local variables, it creates a snapshot of them. Non-local variables are frozen at whatever value they contain when the block is defined, and the block always uses that value, even if the non-local variable changes later on in the program. Watch what happens when we try to change the make variable after creating the block:

1
2
3
4
5
6
7
8
9
NSString *make = @"Honda";
NSString *(^getFullCarName)(NSString *) = ^(NSString *model) {
return [make stringByAppendingFormat:@" %@", model];
};
NSLog(@"%@", getFullCarName(@"Accord")); // Honda Accord
// Try changing the non-local variable (it won't change the block)
make = @"Porsche";
NSLog(@"%@", getFullCarName(@"911 Turbo")); // Honda 911 Turbo

Closures are an incredibly convenient way to work with the surrounding state, as it eliminates the need to pass in extra values as parameters—you simply use non-local variables as if they were defined in the block itself.

Freezing non-local variables as constant values is a safe default behavior in that it prevents you from accidentally changing them from within the block; however, there are occasions when this is desirable. You can override the const copy behavior by declaring a non-local variable with the __block storage modifier:

1
__block NSString *make = @"Honda";

This tells the block to capture the variable by reference, creating a direct link between the make variable outside the block and the one inside the block. You can now assign a new value to make from outside the block, and it will be reflected in the block, and vice versa.

Using Blocks as Method Parameters

Storing blocks in variables is occasionally useful, but in the real world, they’re more likely to be used as method parameters. They solve the same problem as function pointers, but the fact that they can be defined inline makes the resulting code much easier to read.

FYI - A function pointer is part of the C language, but you won’t see them used much. The problem is you can’t use a function pointer in an Objective-C method call.

The standard frameworks are full of block use cases. A very common example: UIView uses a block to define the final state of an animation via the animateWithDuration:animations: method.

Watchout for Self Retain Cycles!

A subtle problem can occur if you need to refer to self within the block – for example:

1
2
3
4
5
6
7
8
9
[UIView animateWithDuration:1.0 animations:^{
[warningView setFrame:self.onscreenFrame];
} completion:^(BOOL finished) {
[self methodToRunAfterAnimationCompletes];
}];

This would cause a compiler error (even when compiling under ARC) because the block will retain self, and this raises the possibility of a retain cycle.

To prevent this, you need to modify the way you pass in the reference to self:

1
2
3
4
5
6
7
8
9
10
11
__weak MySelfType *weakSelf = self;
[UIView animateWithDuration:1.0 animations:^{
[warningView setFrame:weakSelf.onscreenFrame];
} completion:^(BOOL finished) {
[weakSelf methodToRunAfterAnimationCompletes];
}];

Summary

Blocks provide much the same functionality as functions. The fact that they can be defined inline makes it easy to use them inside of method calls, and since they are closures, it’s possible to capture the value of surrounding variables with no additional effort.