Objective-C Guide For Developers, Part 6

In this last part of the guide, we are going to have a look at blocks and how they work.

Table of contents

What blocks are useful for
Declaring and using blocks
Passing blocks as parameters to methods
Capturing the context
Blocks and memory management


What blocks are useful for

Many (but not all) modern languages have what is usually called lambdas or closures (although these two terms are not equivalent, as we will see below, people sometimes use them interchangeably). For a long time Objective-C lacked such feature, but luckily in recent years blocks were introduced to fill the gap. I would say that they are not as elegant as in other languages because of they awkward syntax, but they are still extremely useful.

For those who don’t know the concept yet, lambdas (and blocks) are nameless functions that can, in the code, be treated also as data. What this means is that you can take a piece of functionality and store it in a variable to use it later, or pass it as a parameter to a method. This comes handy in different ways .

The first useful use case for blocks is when you need to provide asynchronous callbacks. Examples for this are tasks that take some time to produce a result, like downloading data from a network connection or knowing when an animation ends. When these tasks are completed, we usually need to have some specific code to be executed. The way this was done before the introduction of blocks was to create a delegate protocol, declaring methods for the callback or callbacks needed. The object that needs to be notified then conforms to this protocol, setting itself as a delegate on the notifying object, before the call that starts the asynchronous task. When the task is completed, the performing object calls the callback or callbacks defined by the protocol on the delegate. As you can see, this usually requires a number of steps, while It would be nicer to just say indicate what code to execute at the end of the task, without having to deal with protocol and delegates. One way could be to pass a selector as a parameter (a selector is the signature of a method), but this is a limited approach we are not going to explore, because delegate protocols have always been the standard way of solving this problem. Blocks allow us to skip creating a new protocol altogether, because, as said, they allow us to a function directly as a parameter of a method, providing the code to execute at the end of a task.

Another use for blocks is to enable methods to be more customizable than they are through normal parameters. To provide a common simple example, let’s say we have to sort an array of objects. The sorting algorithm is going to be fixed, but the way in which we compare objects between themselves to sort them is going to be different for different types. One way to solve this is to implement a comparison method inside the objects themselves (which is a common approach. This is done in Objective-C by overriding the -isEqual: method). Another way to do accomplish this using blocks is to pass to the sorting algorithm the actual function it should use to compare elements.

Declaring and using blocks

Blocks are functions, so when we declare them we have to indicate a return type and the parameters that the block might need. Blocks are declared using the ^ operator. This is the syntax to declare a block variable:

returnType (^variableName)(parameterTypes);

As you can see, the name of the variable needs to be indicated inside parentheses after a ^ symbol, which is what makes the syntax a bit awkward. Like in a normal function or methods, the parameters of the block are separated by a comma and can be omitted for blocks without parameter (using the void keyword), and the return type of the block can be void for blocks that don’t return any value. The parameters do not need to be named in the declaration (but can) and only their type needs to be provided. This is how a block is declared. To create one, the ^ operator is again used, to indicate the beginning of a block literal. After that the parameters of the block are declared between parentheses (this time with names), followed by the block body placed between curly braces.

blockVariable = ^(parameters) {
    ...
};

Pay attention to the semicolon after the closing brace, which you are probably not used to see. Since this is an assignment, it needs a semicolon at the end like any other statement. As with any other variable, declaration and assignment can be composed in a single statement:

returnType (^variableName)(parameterTypes) = ^(parameters) {
    ...
};

You use a block the same way you would use a function, calling it by its name (which is, in this case, the variable’s name, since a block is by definition an anonymous function) followed by parameters inside parentheses. There is one caveat to pay attention to, though. Unlike what happens with objects, where it’s possible to call methods on nil values with no adverse effect, calling a nil block will result in an exception and a crash. For this reason, you often need to guard a block call with an if statement:

if (block) {
    block(parameters);
}

Let’s see an example. We will declare and use a block that triples a number passed as a parameter. It’s not a very useful or common block by itself, but it allows us to have a look at a simple case, since the syntax for blocks is a bit uncommon.

NSInteger (^triple)(NSInteger) = ^(NSInteger number){
    return number * 3;
};
NSInteger six = triple(2);

Notice that, as stressed, triple is not the name of the block itself, but just the name of the variable that contains it. This means we can assign a completely different block to the variable and the compiler will not complain, provided it has the same return type and parameters.

triple = ^(NSInteger number){
    return number - 4;
};
NSLog(@"%i", triple(10)); // Prints 6

If you want to store a block in an object property to use it later, the property can be declared using the same syntax used for block variables:

@property returnType (^propertyName)(parameterTypes);

Since the blocks syntax is a bit hard to remember and error prone, Apple recommends in the documentation to define a new type when we need a block, to improve readability.

typedef returnType (^typeName)(parameterTypes);
typeName variableName;

The previous example becomes:

typedef NSInteger (^Block)(NSInteger);
Block triple = ^(NSInteger number){
    return number * 3;
};

Just pick a better type name than the one I put in the example.

Passing blocks as parameters to methods

Blocks become really useful when you can pass them as parameters to methods, to provide callbacks or customization. The syntax to declare a block parameter in a method is as follows:

- (void)methodName:(returnType (^)(parameterTypes))blockParameterName;

The syntax gets a bit more awkward and harder to remember because the ^ operator in this case is alone in the parentheses, because the variable name has moved to the usual place for parameter names in a method. If a method that uses blocks has more than one parameter, it is good practice to declare the block parameters last, to make the method call more readable. When calling a method with a block parameter, a block variable can, of course, be passed as a parameter, but a block literal can be directly passed inline as well. When using autocompletion in Xcode you can hit the return key on the parameter placeholder to make Xcode expand it with a block literal, that you will just need to fill with instructions. This is how to call a method with an inline block:

[methodName:^(parameters) {
    ...
}];

One good example of use of blocks as method parameters, for both customizing a method and as callbacks, is the UIView animation API. One of the methods in the API has this declaration:

+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;

The second parameter is a block with no return type and no parameters, which is used to provide the changes to the view hierarchy that will be animated. This is an example of method customization: the animation routine always performs the same steps in preparing the environment to perform the animation and in tearing it down after the animations have been performed. What is always different are the animations themselves, which could not be passes simply as data (or at least, not without complicate constructs ). The last parameter of the animation method is the callback that will be executed when the animation stops (it has a parameter to know if the animation was interrupted or not). This is how you call this method:

[UIView animateWithDuration:0.2 animations:^{
    ...
} completion:^(BOOL finished) {
    ...
}];

Capturing the context

One of the useful features of blocks is that they can capture values from the context that surrounds them when they are created, and be able to reference them when the block is actually executed. This is called “closing over the environment” and it’s why lambdas are sometimes called closures (the technical difference is that the former are only anonymous functions, without closing). What I mean here with “context” or “environment” are the variables that defined in the lexical scope enclosing the block, outside the block itself. These variables are copied and stored inside of the block. This means that the block has at its disposal a snapshot of the context in which it was created that he can reference at the moment of its execution, even if that context is long gone or has changed. Let’s see a simple example.

NSInteger value = 3;
    NSInteger (^doubleValue)(void) = ^{
    return value * 2;
};

value = 5;
NSLog(@"%i", doubleValue()); // Prints 6

As you can see, when the block is executed, it uses the value 3, which is the old value of the value variable, although in the meantime it has change to 5. Local variables captured in a block are copied, which means that if we change them inside of the block, their value won’t be changed outside of it.

NSInteger value = 3;
NSInteger (^assign)(void) = ^{
    value = 5;
};

assign();
NSLog(@"%i", value); // Prints 3

In case of object variables, what is copied is only the reference to the object, which means we are still able to change the object properties from inside the block. Sometimes though, we might want to be able to change an external variable from inside of a block. This can be done by using the __block keyword:

__block NSInteger value = 3;
NSInteger (^assign)(void) = ^{
    value = 5;
};

assign();
NSLog(@"%i", value); // Prints 5

Blocks and memory management

Regarding memory, blocks behave like objects. When a new block is created, it is allocated on the heap like any other object and handled by ARC in the same way. If a block captures a reference to an object, that reference is going to be a strong one, since the object referenced needs to be kept in memory until the block is in memory too and can be executed. This, as it happens for object, can lead to retain cycles, causing memory leaks. One particularly tricky case is when the block captures the self reference of an object. This often goes unnoticed by the developer, because we are usually using the self keyword inside the block not by itself, but to access some property. An example explains this better than words:

@interface Logger : NSObject

@property NSString message;
@property void (^loggingBlock)(void);

@end

@implementation

- (void)setLoggingBlock {
    void (^block)(void) = ^{
        NSLog([self.message]);
    }
    self.loggingBlock = block;
}

- (void)printLogMessage {
    if (self.loggingBlock) {
        self.loggingBlock();
    }
}

@end

This might look innocuous at a first glance, and that’s why it’s a tricky case. In the -setLogMessage: we are creating a block that logs the message property. Since we need the block later in the printLogMessage method, we store it in another property. What we might think the block does is capture the reference to the message property (an thus to an instance of NSString). What actually happens though is that the block captures a reference to self instead. Since the block is then retained in a property inside the object itself, this creates a strong reference cycle. How do we break this? The solution is not very elegant, but does the job:

- (void)setLoggingBlock {
    __weak id weakSelf = self;
    void (^block)(void) = ^{
        Logger *strongSelf = weakSelf;
        NSLog([strongSelf.message]);
    }
    self.loggingBlock = block;
}

As I said, this mistake is hard to spot and you need to be constantly vigilant not to introduce strong reference cycles in your code. Luckily there is a warning in Xcode that tells you when you are making this mistake, but it is off by default, so you have to go and enable it explicitly in the project build settings.

Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+