Being a Blockhead

by Ed on June 20, 2010

Last time I talked about asynchronous behavior via Grand Central Dispatch. One of the core features that makes this all possible is blocks. I thought I’d take a step back this time and just talk about blocks themselves and how they can be used by your code.

The Ultimate Callback

If you recall, blocks are snippets of code you can run which capture information about their environment as needed. That is, they behave like closures in other languages such as Javascript.

Because of this, they are the ultimate callback. You can pass parameters to them, and they can reference variables in the current scope the block is introduced in. You can also pass blocks around as objects, and copy them as needed, as we’ll see later.

We’ve already seen the most basic callback in use in the last post, where we had a simple block that looked like this:

^{ printf("this is a block"); }

This is what you’d use for a call to dispatch_async, etc. This is essentially a block that takes no parameters and returns no result. You can actually typedef such a thing as follows:

typedef void (^GenericCallback)(void);

Or just define the types directly as:

void (^)(void) myCallback;

It’s pretty much the exact same syntax as a function pointer, but you substitute a ^ for the *. But blocks can also take parameters and return values:

NSString *(^)(int a, NSString *b);

In fact, some of the newer APIs take blocks now instead of using callbacks or target/selector. Consider this method in NSArray.h:

- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx,
                 BOOL *stop))block;

This method allows you to iterate over all items in the array. Each time your block is called, you get the object, the index, and a stop parameter (if you want to terminate iteration). You can use it as such:

[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    NSLog(@"Item %d is %@", idx, obj);
}];

So in its most basic use, it becomes an inline callback. But unlike a normal callback, it has the advantage of context. Let’s say we have an object’s identifier. I need to compare that to each item in an array’s identifier until we find a match. You can do the following:

NSUInteger index;
NSString *myIdentifier = myObject.identifier;

index = [myArray indexOfObjectPassingTest:
                 ^(id obj, NSUInteger idx, BOOL *stop) {
                    return ([obj.identifier isEqualToString:myIdentifier]);
                 }];

The advantage here obviously is that we don’t need to pass any sort of ‘userData’ or ‘refCon’ along with the block. That comes for free because myIdentifier is in scope as far as the block is concerned. Clearly this is far more powerful than ye callbacks of olde.

As the implementer of such a function, you call blocks just like you call a function. In this example, we’ll define a contrived method that takes a math function that operates on its members.

- (int)performMathFunction:(int (^)(int a, int b))operation {
    return operation(_a, _b);
}

// then someplace later...
int result1 = [self performMathFunction:^(int a, int b) { return a + b; }];   
int result2 = [self performMathFunction:^(int a, int b) { return a / b; }];

Kind of useless in and of itself, but the point is clear in that you can use a block where you might have otherwise used a callback, but now you have the advantage of allowing the callback to take its data with it.

Blocks as Objects

Another benefit is that you can hold onto blocks as you would objects. There are two methods defined for copying and releasing blocks: Block_copy and Block_release. These are handy if you want to allow a block to be kept around by an implementation to be called later. This is ultimately what dispatch_async uses to take your block and run it over on some other thread. You only generally need to use it if you will be running the block after the function that passed the block will have been exited by the time it is called.

Please note that if you are using a synchronous function such as the NSArray methods above, you don’t need to do any copy/release because it’s all happening right then and there in your thread. This is only for those cases where you want to for whatever reason call a block sometime later.

When using Objective-C, you can treat them like almost any other object and just use copy and release. For example, another contrived example:

@class MyObject;

typedef void (^MyCompletionHandler)(MyObject* object);

@interface MyObject : NSObject {
    ...
    MyCompletionHandler        _completion;
}

@property(nonatomic, copy) MyCompletionHandler   completionHandler;

@end

@implementation MyObject

...

// Implementing this just to show it in action, 
// but you could just use @synthesize
- (void)setCompletionHandler:(MyCompletionHandler)completion {
    [_completion release];
    _completion = [completion copy];
}

...

- (void)taskIsComplete {
    _completion(self);
}
@end

When you copy a block, it’s context goes with it. If you copy a block in Objective-C, the objects referenced therein will get retained when the block is copied. We touched on this last time. Remember though that in C (or really, when using C types like CFArrayRef), this does not happen, and you will need to retain objects before your block and release them inside to ensure that when the block is run it really has a reference to the object.

Be Cautious of Loops

This copying/retaining semantics of blocks is very useful as it means your objects won’t go away while your block is being executed on some other thread, but you can also get into trouble if you happen to be using them only on one thread and the context in a block contains a reference to some parent structure. You’ll get into a retain-loop. Consider a UITableViewCell subclass that you want to initialize with a block to perform some action:

// inside your controller's tableView:cellForRowAtIndexPath:
cell = [[MyCell alloc] initWithBlock:^(MyCell* cell) {
           [self adjustCell:cell];
       }];

So here we have a block that calls a method on the controller. Simple enough. But when that cell is created, it’s init method might copy the block to ensure the block is valid at all times. The problem is, it references self. But that self is the view controller. So you have a view controller retained by one of its descendants. This creates a retained object loop that ultimately means this controller will never get released.

The only way I’ve found to deal with this is by creative use of __block variables. But we haven’t talked about that, now have we? Guess it’s time…

Modifying Local Variables in a Block

While blocks can reference variables in the context they are defined, those variables are read-only. This is done (I presume) for efficiency’s sake, and 99 times out of 100 this is fine. However, there are times when you want to change the value of one of those variables. The way you do this is to prefix them with the __block modifier. Consider:

int     x = 10;

dispatch_sync(aQueue, ^{
    x = x + 10;
    printf("block says x is %d\n", x);
});

printf("x is %d\n", x);

Yet another contrived example, but here we want to call a block via dispatch_sync. Unlike dispatch_async, dispatch_sync executes code synchronously. You might wonder why you’d do that. Well, you’ll have to wait until another blog post ;-) In any case, this block wants to modify the value of x. The output of this is:

block says x is 20
x is 10

Not quite what you expected, perhaps. The variable x was perfectly mutable inside the block, but it never affected the original scope. We can fix this easily just by adding __block:

__block int x = 10;

And then our output is what you’d expect:

block says x is 20
x is 20

And all’s right with the world again.

But how does this knowledge help us avoid the retain cycle we saw above? Well, it turns out that __block variables are actually not retained when the lock is copied. So you can do this:

// inside your controller's tableView:cellForRowAtIndexPath:
__block me = self;
cell = [[MyCell alloc] initWithBlock:^(MyCell* cell) {
           [me adjustCell:cell];
       }];

Since the self doesn’t get retained this time, we avoid the retain cycle. While it strikes me as iffy, it does work, and it’s the only way I’ve found to deal with this situation. You can’t simply avoid copying the block if you want to call it later, because the scope will be gone by the time the function that declared the block exits. That will lead to nothing but sadness.

Now You’re a Blockhead, Too

At this point, you know just about everything there is to know about blocks. Some of what we’ve talked about here is going to help us when we discuss using blocks and dispatch queues to implement synchronization in a future post. Believe me, GCD does it all. It’s one technology that might convince you to make your apps depend on it completely to the point of requiring it to run. Doing the types of things GCD can do any other way seems so… primitive.

{ 2 comments… read them below or add one }

Dan Prince January 27, 2011 at 5:45 pm

Thanks Ed, I love using blocks and this is a really nice set of examples.

Reply

Kiren Paul July 9, 2012 at 9:06 pm

Ed Thank you for sharing your knowledge with us …

Reply

Leave a Comment

{ 1 trackback }

Previous post:

Next post: