Recursive Blocks in Objective-C

Oct 29 2011

Blocks are an awesome feature added to Objective-C with Mac OS X 10.6 and iOS 4.0.

Most of the Cocoa API now includes block-based alternatives and some new APIs (like the AssetsLibrary framework for example) are only based on blocks. So you do not have a choice anymore, you have to use blocks.

One cool feature that you might run into is recursive blocks. Even if there is in fact no reason why a block could not be recursive, there are a couple of rules to follow. An example of a recursive block would be something like the following:

void (^blocky) ();
blocky = ^ void (){
    blocky();
};
blocky();

Note that this piece of code has a couple of problems, apart from the fact that it loops forever, but explaining them being the point of this post we will discuss them later (no spoilers!).

So why would we ever want to use a block recursively? Well, think about a common pattern in Cocoa, particularly in CocoaTouch. You have a UINavigationController on which you push instances of subclasses of UITableViewController in order to display some sort of tree structure of data. This structure will most likely be fetched from Core Data and resides in a single place in your application, probably in the root view controller of your navigation controller.

Now suppose that each of these instances of (subclasses of) UITableViewController has to perform a specific action when one of its cells is clicked. It could be modifying the cell, pushing a new UITableViewController on the navigation stack or even do nothing. Since, as we have seen, the whole data structures would be owned by a single object (the root view controller), you do not really want controller down the stack to have to decide what to do with this data. You would rather have the root view controller, that actually manages the data, react to events down the chain.

You could have a simple protocol in your UITableViewController subclass that the root view controller would conform to but you would end up with your logic for cell selection handling residing in two spots: in the table view cell selection callback in your root view controller and in the table view controller subclass delegate methods in this very same root view controller.

So why not having a block that handles the cell selection in the root view controller and passing this block down the stack. Once a cell down the navigation controller is clicked, the controller subclass simply invokes that block and the root view controller will perform anything it has to do at that stage.

So let's write some (pseudo)code. First, assume that we will use the same subclass of UITableViewController for any level of navigation down the stack. At the end, it probably will display the same kind of data so why create a custom subclass for each level of navigation. We will also have a simple class representing a data object to be displayed. Think about it as the data backing up the content of your cells. These objects are probably organized in an NSArray that itself resides in the root view controller. Each data object has a children array containing other data objects further down in the data hierarchy.

First the very elementary MyDataObject class:

@interface MyDataObject : NSObject

@property (strong) NSArray children;

@end

@implementation MyDataObject

@synthesize children = _children;

@end

Next, the UITableViewController subclass used to manage the table view used to display content down the navigation stack:

typedef void (^CellSelectionHandlerBlock)(MyDataObject clickedItem);

@interface MyTableViewControllerSubclass : UITableViewController

@property (strong) MyDataObject dataObject;
@property (copy) CellSelectionHandlerBlock selectionHandlerBlock;

@end

@implementation MyTableViewControllerSubclass

@synthesize dataObject = _dataObject;
@synthesize selectionHandlerBlock = _selectionHandlerBlock;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (_selectionHandlerBlock != NULL)
    {
        MyDataObject *clickedDataObject = [[[self dataObject] children] objectAtIndex: [indexPath row]];
        _selectionHandlerBlock(clickedDataObject);
    }
}

// some other methods, in particular UITableView dataSource and delegate

@end

And finally the root view controller, itself a subclass of UITableViewController, that manages the table view used at the first level of hierarchy. It also manages the data objects and handle the cell selection in both its own table view and down the stack:

@interface MyRootTableViewController : UITableViewController

@property (strong) NSArray *dataObjects;

@end

@implementation MyRootTableViewController

@synthesize dataObjects = _dataObjects;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    CellSelectionHandlerBlock selectionHandlerBlock = nil;
    selectionHandlerBlock = ^ void (MyDataObject clickedItem) {
        if ([[clickedItem children] count] > 0)
        {
            MyTableViewControllerSubclass *viewController = [[MyTableViewControllerSubclass alloc] init];
            [viewController setDataObject: clickedItem];
            [viewController setSelectionHandlerBlock: selectionHandlerBlock]
            [[self navigationController] pushViewController: viewController animated: YES];
        }
        else
        {
            NSLog(@"Do some funky stuff with the cell!");
            // like presenting a modal view controller with some content.
        }
    }
    
    MyDataObject *dataObject = [[self dataObjects] objectAtIndex: [indexPath row]];
    selectionHandlerBlock(dataObject);
}

// some other methods, in particular UITableView dataSource and delegate

@end

So as you can see, we only need to write the selection handling code once. In function of the data object underlying the clicked cell, the root view controller create a new MyTableViewControllerSubclass with the appropriate data object and pass the selection handler block to it. Whenever a cell is clicked in a view controller down the stack, we first retrieve the data object corresponding to the clicked cell and invoke the block.

Also, every time a table view cell is clicked in the root view controller itself, the very same block is invoked.

However, this piece of code creating the block is wrong for two reasons that I am going to explain. As you can see, the block is recursively called inside the block itself. In order to explain the problems with the current approach, let's get back to our simpler blocky example.
In this example, a block is created and then called inside the content of the block creating infinite recursion. Leaving aside the infinite part here, this piece of code would crash on the first recursive call. To understand why, let's first discuss how a block treats its enclosing scope variables. Consider the following example:

int i = 1;
void (^block) () = ^ void () {
    NSLog(@"i = %i", i);
    i++;
};

This piece of code would correctly print i = 1 but would crash on the second line i++. The reason is that the block captures local variables in its scope and treat them as constant.
In the case of Objective-C objects, captured objects are retained (and automatically released when the block goes away).
Blocks are actually Objective-C objects themselves. When first created, a block lives on the stack. If you intend to use the block beyond the current scope, you need to copy it to the heap. Once the block has been copied to the heap, it can be retained and released as a regular object (it does not make sense to retain a stack-based block though).
In order for a block to change its enclosing scope, we can use the __block storage type. Consider the following example:

__block int i = 1;
void (^block) () = ^ void () {
    NSLog(@"i = %i", i);
    i++;
    NSLog(@"i = %i", i);
};

In this case, the program will run correctly and print i = 1 and i = 2. The block was able to modify the variable in its enclosing scope. Also note that __block captured objects are not retained.

So coming back to our blocky example, we now understands better why the crash is occurring. Since the block itself is invoked inside the block, it is const copied even before the assignment occurs. We can fix this by rewriting the program as:

__block void (^blocky) ();
blocky = ^ void (){
    blocky();
};
blocky();

Similarly, in our navigation controller example, we would have to rewrite:

__block CellSelectionHandlerBlock selectionHandlerBlock = nil;
selectionHandlerBlock = ^ void (MyDataObject clickedItem) {
    ...
}

So far so good, and to be honest, during my testing, I did not have to do anything else for this to run perfectly in Debug mode. However, the first time I ran it in Release mode, my program crashed on the block invocation. It took me a while to understand what was going on until I realized that there was a small problem here.
I was reusing my block outside the current scope and even copied it later on (when passed to another objects).
If you think about it, the first block that is assigned to the __block type variable is a stack-based block. The block that is called inside the block is this very same stack-based block.
When the block is moved to the heap, captured variable are moved to the heap too. So our block variable is moved to the heap once the block is copied so we need to make sure that the reference variable points to the copy of the block and not the original stack-based one.

The solution to this problem is to copy the block while creating it and assign this copied version to our __block variable:

__block void (^blocky) ();
blocky = [^ void (){
    blocky();
} copy];
blocky();

I have no idea why this was not crashing in Debug.

Blocks are very handy but are a tricky concept to grasp at first. However, there are a very powerful tool and I can only tell you that once you go block you never go bock (sorry, that was lame).

I definitely recommend the following articles and books to learn more:

blog comments powered by Disqus