Using Blocks in iOS 4: Designing with Blocks

September 15, 2010

In the first part of this series, we learned how to declare and call basic Objective-C
blocks. The motivation was to understand how to effectively use the new APIs in iOS 4 that
take blocks as parameters. In this installment we're going to shift our focus toward
writing our own methods that take blocks. By understanding how to use blocks in your own
code, you'll have another design technique in your repertoire. And you might just find that
blocks make your code easier to read and maintain.

Writing Methods That Take Blocks

We left off last time with a challenge: Write a Worker class method that takes a block and repeatedly calls it a given number of times, passing in the iteration count each time. If we wanted to triple the numbers 1 through 5, for example, here's how we would call the method with an inline block:

[Workerrepeat:5withBlock:^(intnumber){returnnumber*3;}];

I often design classes this way. I start by writing the code that calls a fictitious method
I need, simply as a way of shaping the API before committing to it. Once the method call
feels right, I go ahead and implement the method. In this case, the method name
repeat:withBlock: just doesn't feel right to me. (I know, that's the name I
used in the previous article, but I've changed my mind.) The name is confusing because the
method doesn't actually repeat the same thing. It iterates from 1 to the given number, and
passes the block the iteration count.

So let's get started on the right foot by renaming it:

[WorkeriterateFromOneTo:5withBlock:^(intnumber){returnnumber*3;}];

There, I'm pretty happy with a class method named iterateFromOneTo:withBlock:
that takes two parameters: an int representing the number of times to call the
supplied block and the block itself. Now let's implement the method.

For starters, how do we declare the iterateFromOneTo:withBlock: method? Well,
we need to know the types of both parameters. The first parameter is easy; it's an
int. The second parameter is a block, and all blocks have an associated type.
In this example, the method will take any block that accepts a single int
parameter (the iteration count) and returns an int value as the result. Here's
the actual block type:

int(^)(int)

Given the name of the method and its parameter types, we're ready to declare the method. This being a Worker class method, we declare it in the Worker.h file:

Now, block parameters can be difficult to parse at first sight. The trick is to remember that all method parameters in Objective-C have two components: the parameter type in parentheses followed by the name of the parameter. In this example, the limit parameter is an int type and the block parameter is an int (^)(int) block type. (You can name the parameter whatever you like; block isn't a special name.)

The implementation of the method over in the Worker.m file is pretty straightforward:

It spins through a loop, calling the block with the iteration count each time through the
loop and printing the block's result. Remember that once we have a block variable in scope,
calling the block is just like calling a function. In this case, the block
parameter references a block. So when block(i) is executed on the highlighted
line, it invokes the code in the block. When the block returns, control picks back up on
the next line.

We can now call the iterateFromOneTo:withBlock: method with an inline block, like so:

[WorkeriterateFromOneTo:5withBlock:^(intnumber){returnnumber*3;}];

Alternatively, instead of using an inline block, we could pass the method a predefined block variable of the appropriate type:

Typedef Is Your Friend

Declaring block types can get messy in a hurry. Even in our simple example the function pointer syntax leaves a lot to be desired:

+(void)iterateFromOneTo:(int)limitwithBlock:(int(^)(int))block;

Imagine the block taking multiple parameters, some of which are pointer types, and you're quickly in for a world of hurt. To improve readability and eliminate duplication in the .h and .m files, we can revise our Worker.h file to use a typedef, like so:

A typedef is a C keyword that assigns a synonym to a type. Think of it as the
nickname for a type that has a cumbersome real name. In this case, we've defined
ComputationBlock to refer to a block type that takes an int and
returns an int. Then, when defining the
iterateFromOneTo:withBlock: method, we simply use
ComputationBlock as the block parameter type.

Likewise, in the Worker.m file, we can simplify the code by using ComputationBlock:

Ah, much better! The code is easier to read and doesn't duplicate the block type syntax across files. In fact, you can use ComputationBlock in place of the actual block type anywhere in your application that imports Worker.h.

You'll run into similar typedefs in the new iOS 4 APIs. For example, the ALAssetsLibrary class defines the following method:

You can then use ALAssetsLibraryAssetForURLResultBlock and ALAssetsLibraryAccessFailureBlock within your application to refer to the respective block types.

I recommend always using a typedef when writing a public method that takes a block. It helps keep your code tidy and expresses the intent of the block to developers who may use your code.

Another Look At Closures

You may recall that blocks are closures. We briefly touched on closures in the first part of this series, but the example wasn't particularly interesting. And I hinted that closures would become more useful when we started passing blocks around to methods. Well, now that we know how to write methods that take blocks, let's try another closure example:

We're using the same iterateFromOneTo:withBlock: method we wrote earlier, but
in this example the block has a subtle, yet very important, difference. Rather than
hard-coding the multiplier inside the block as in previous examples, this block uses a
local multiplier variable declared outside of the block. The result of running
the method is the same as before; it triples the numbers 1 through 5:

iteration1=>3iteration2=>6iteration3=>9iteration4=>12iteration5=>15

That this code runs at all is an example of the power of closures. The code defies typical
scoping rules. In particular, consider that the multiplier local variable is
out of scope when the block is called from inside the
iterateFromOneTo:withBlock: method.

Remember, however, that blocks also capture their surrounding state. When a block is
declared it automatically takes a (read-only) snapshot of all the variables in scope that
the block uses. Because our block uses the multiplier variable, the variable's
value is captured in the block to be used later. That is, the multiplier
variable has become a part of the state of the block. And when the block is passed to the
iterateFromOneTo:withBlock: method, the block's state goes along for the ride.

OK, so what if we wanted to modify the multiplier variable inside the block?
Say, for example, each time the block was called we wanted the multiplier to become the
result of the last computation. You might be tempted to just assign to
multiplier inside the block, like this:

But the compiler won't let you get away with that. You'll get the error "Assignment of read-only variable 'multiplier'". This happens because a block effectively gets a const copy of stack (local) variables that it uses. These variables are immutable inside the block.

If you want to be able to modify an externally-declared variable inside a block, you need to prefix the variable with the new __block storage type modifier, like so:

It's important to note that after the block runs, the value of the multiplier
variable has been changed to 360. In other words, the block doesn't modify a copy of the
variable. Any variable declared using the block modifier is passed by
reference into the block. In fact, block variables are shared with all other
blocks that may use that variable. A word of caution is in order here:
block is not to be used casually. There is a marginal cost involved in
moving things to the heap and, unless you really need to modify a variable, you shouldn't
just make it a block variable.

Writing Methods That Return Blocks

Every once in a while it's handy to write a method that creates and returns a block. Let's look at a contrived (and dangerous!) example:

The block we get back remembers that we want everything raised to the power of the exponent (2 in this case). When we call the block with a number, it should return the result of raising the number to the power of the exponent. As it stands, though, if we were to run this code it would blow up with an EXC_BAD_ACCESS runtime error.

What gives? Well, the key to fixing the problem lies in understanding how blocks are
allocated. A block starts its life on the stack because allocating memory on the stack is
relatively fast. Stack variables, however, are destroyed when they're popped off the stack.
This happens when returning from a method.

Looking back out our raisedToPower: method, we see that we're creating a block (on the stack) and returning it. That in turn causes the scope within which the block was declared to be destroyed, which includes the block. So when we go to use the returned block variable, it's a time bomb.

The fix is to move the block from the stack to the heap before returning it. That sounds complicated, but it's actually very easy. We simply call copy on the block and the block will be automatically moved to the heap. Here's the revised method that works as expected:

Notice that since we called copy, we must balance it with an
autorelease in this case to be good stewards of memory. Otherwise the calling
code would need to remember to release the block, which goes against the
ownership conventions.

It's not often that you need to copy a block, but returning a block allocated inside a method is one case where it's critical to make a copy to move the block to the heap. Be careful out there!

Putting It All Together

OK, let's put some of what we learned together into a slightly more practical example.
Suppose we're designing a simple class that will play movies. Users of this class want to
receive a callback when a movie is done playing so they can perform application-specific
logic. It turns out that blocks are a convenient way to handle callbacks.

Let's start by writing the code from the perspective of a developer using this class:

So we need a MoviePlayer class with two methods:
initWithCallback: and playMovie:. The inititializer needs to take
a block, stash it away, and then call the block at the end of the playMovie:
method. The block takes one parameter (the movie title) and returns nothing. We'll
typedef the callback block type and use a property to hang onto the callback
block. Remember, blocks are objects and you can use them as instance variables and/or
properties. We'll use a property in this case to demonstrate the point.

#import "MoviePlayer.h"@implementationMoviePlayer@synthesizecallbackBlock;-(id)initWithCallback:(MoviePlayerCallbackBlock)block{if(self=[superinit]){self.callbackBlock=block;}returnself;}-(void)playMovie:(NSString*)title{// play the movieself.callbackBlock(title);}-(void)dealloc{[callbackBlockrelease];[superdealloc];}@end

In initWithCallback:, we assign the supplied block to the
callbackBlock property. Because the property was declared to use
copy semantics, the block is automatically copied, which moves it onto the
heap. Then when the playMovie: method is invoked, we call the block passing in
the movie title.

Now let's say a developer wants to tie our MoviePlayer class into an application that manages a queue of movies you plan to watch. Once you've watched a particular movie, it should be removed from your queue. Here's a trivial implementation that also demonstrates a closure:

NSMutableArray*movieQueue=[NSMutableArrayarrayWithObjects:@"Inception",@"The Book of Eli",@"Iron Man 2",nil];MoviePlayer*player=[[MoviePlayeralloc]initWithCallback:^(NSString*title){[movieQueueremoveObject:title];}];for(NSString*titlein[NSArrayarrayWithArray:movieQueue]){[playerplayMovie:title];};

Notice that the block uses the local movieQueue variable, which becomes part of the state of the block. When the block is called it removes the movie title from the movieQueue array even though it's out of scope by that time. After all the movies have been played, the movieQueue will be empty.

A couple important things worth noting here:

The movieQueue variable is an array pointer, and we're not modifying where it
points. We're modifying its contents. Therefore, we don't need to use the
__block modifier.

To iterate through the movie queue we needed to create a copy of the movieQueue variable. Otherwise, if we used movieQueue directly, we'd be removing elements while trying it iterate through it. This causes an exception because enumeration in Objective-C is designed to be safe.

Instead of using a block, we could have declared a protocol, written a delegate class, and registered the delegate as a callback. Using an inline block in this example is simply more compact and convenient.

New functionality can be added without changing the MoviePlayer class. Another developer might pass in a block that tweets the movie title to your Twitter account, marks the title as being played in a Core Data store, or prompts you to rate the movie.

Next Steps

That's a wrap for this series. Thanks for tuning in! There's more to blocks, but what we've learned so far represents the majority of uses. If you want to dig even deeper, I suggest working through the following resources:

As a final takeaway, I hope you've seen how blocks offer a different style of programming that can inform the design of your application. When and where you use them is a judgement call, like any other design decision. Give blocks a try, and I look forward to your comments.

Have fun!

(Thanks to Matt Drance (@drance) and Daniel Steinberg (@dimsumthinking) for reviewing drafts of this article.)

iOS Developer Training

Want to learn how to build iOS apps from scratch? Consider attending an upcoming public iPhone/iPad Programming Studio, or scheduling a private course, taught by two experienced iOS developers. You'll come away ready to create your first iPhone/iPad app, or improve your existing app. It's a lot of fun!