A Quick Gotcha About Blocks

Blocks are a great addition to the iOS SDK and C standard, especially for predominantly event driven applications as we commonly see on the iPhone and iPad. There’s a quick gotcha that a junior developer here at Mindgrub reminded me of the other day.

While blocks are a powerful tool, if you’re not careful they can start to degrade the quality of your app by introducing retain cycles. Retain cycles occur when two objects in your application keep strong references to one another. Take the common table view for example. Ordinarily, the view controller containing (owning) the table view acts as the datasource and delegate for table, providing the necessary information for displaying the number of rows and supplying cell objects. If you look closely at the properties on UITableView for datasource and delegate, you’ll see that both properties are declared as “assign” or “weak” in ARC. This is critical particularly in the case where the view controller is also the datasource/delegate. If the properties were “strong” or “retain” you could easily wind up in a retain cycle where neither your UITableView nor your view controller would ever be destroyed, thereby leaking memory.

This can happen with blocks as well and it’s not always so obvious. Let’s say we have a view for which we define a block to handle the user tapping it. The code might look something like this:

Blocks automatically scope capture and retain any object types you use in the block. In this case that variable is “self”. Now we have a retain cycle since self owns the tap view which owns the block which now owns self.

According to Mike Ash, the correct way to handle this is to use a weak pointer (assign) in the block like this:

By using a weak, nonretained pointer along with the __block modifier we’ve broken the retain cycle. For ARC code you should use __weak instead of __block.

There’s two important lessons to be pulled from this. The first is that you always have to be aware of the code you’re writing and look for subtle retain cycles like this one. No amount of automatic reference counting magic can help you in some of these scenarios. The second is that it’s always good to look at code written by others on your team because there’s nearly always something new to learn to refresh on, even if you’re one of the more seasoned developers and the code you’re reading is from a junior dev.

Great post. I recently ran into a retain issue when converting the source for “Picross HD” to ARC, although it wasn’t related to blocks. It was related to a property not being setup as a weak reference.

I didn’t think of this potential case with blocks. Thanks a bunch for sharing.

I also noticed you used the @property copy attribute for tapBlock which is also very important (even with ARC).

Another gotcha with blocks is you need to check that they are not nil before using one, because unlike passing messages to nil Objective-C objects, your app will crash if you try and use a nil block. I pointed this out because I sometimes see people using “empty” blocks when they should really use nil. Attempting to do this in my own API caused me to stumble on this painfully obvious issue.

Objective-C has caused me to become lazy about checking for nil/null in non-logic situations :-)