ARC, Blocks, and retain cycle reminders

I've been neck-deep in Cocoa / iOS / mobile work lately, trying to inhale everything as quickly and thoroughly as possible. Each discovery of a new way of doing things is exciting - in exploring a new ecosystem, it's been a blast being exposed to different design patterns, best practices, and libraries.

A fellow YCer introduced me recently to BlocksKit (and A2DynamicDelegate), two libraries that rejigger the standard delegate pattern of lots of the normal framework classes into a block. For example, consider the difference between the framework-standard code to show a UIActionSheet:

400: Invalid request

And the same logic, with BlocksKit:

400: Invalid request

This feels sturdier, more compact, and more straightforward - especially considering my Ruby background. In integrating BlocksKit into my project, though, I started running into whispers about retain cycles - specifically, a situation in which a class has a strong reference to a block, which in turn has strong references to the local variables (often members of the class!) it might use or call.

After reading a bunch of different blog posts and StackOverflow answers discussing the same issue, I wanted to consolidate / quote a few of the most useful sources. In short, whenever using blocks (especially in ARC-enabled code, which handles most of the memory management for you), developers need to watch out for retain cycles and break them if necessary.

Blocks in Objective-C have one more very important difference from blocks in C in handling variables that reference objects. All local objects are automatically retained as they are referenced! If you reference an instance variable from a block declared in a method, this retains self, as you're implicitly doing self->theIvar.

source: Programming with C Blocks. Click through for code snippets, examples, and a deeper discussion of Memory Management in blocks (section 4.2).

The __block qualifier allows a block to modify a captured variable:

id x;
__block id y;
void (^block)(void) = ^{
    x = [NSString string]; // error
    y = [NSString string]; // works
};

Without ARC, __block also has the side effect of not retaining its contents when it's captured by a block. Blocks will automatically retain and release any object pointers they capture, but __block pointers are special-cased and act as a weak pointer. It's become a common pattern to rely on this behavior by using __block to avoid retain cycles.

Under ARC, __block now retains its contents just like other captured object pointers. Code that uses __block to avoid retain cycles won't work anymore. Instead, use __weak as described above.

source: Mike Ash on ARC - scroll down to the section on blocks.

In retrospect, I read a good number of these posts when I was first exploring iOS and ARC - I suppose it's a testament to how much content is in some of these articles (or the complexity of changing ecosystems?) that I didn't make all of the connections / pick up all of the tips that I should have.

You can use lifetime qualifiers to avoid strong reference cycles. For example, typically if you have a graph of objects arranged in a parent-child hierarchy and parents need to refer to their children and vice versa, then you make the parent-to-child relationship strong and the child-to-parent relationship weak. Other situations may be more subtle, particularly when they involve block objects.

In manual reference counting mode, __block id x; has the effect of not retaining x. In ARC mode, __block id x; defaults to retaining x (just like all other values). To get the manual reference counting mode behavior under ARC, you could use __unsafe_unretained __block id x;. As the name __unsafe_unretained implies, however, having a non-retained variable is dangerous (because it can dangle) and is therefore discouraged. Two better options are to either use __weak (if you don’t need to support iOS 4 or OS X v10.6), or set the __block value to nil to break the retain cycle.

source: Use Lifetime Qualifiers to Avoid Strong Reference Cycles - click through for code examples. Unsurprisingly, Apple's own documentation is the most thorough - if only I'd found it first!

Specifically, I wanted to know how best to handle the case in which I called -dismissViewControllerAnimated:completion: when the user, say, clicked the "Done" button on the toolbar:

400: Invalid request

Thanks to the Apple docs, we have instead:

400: Invalid request

Time for a code audit - I hastily (aka giddily, aka excitedly) integrated BlocksKit yesterday without researching this too closely, but I know that I use blocks often elsewhere in my code, and it's time to make sure everything's correct before I push my latest set of commits.

Let me know what you think on Twitter.