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:


#import <UIKit/UIKit.h>

typedef void(^TapBlock)(void);

@interface TapBlockView : UIView
{
   TapBlock tapBlock;
}

@property (nonatomic, copy) TapBlock tapBlock;
@end

@implementation TapBlockView
@synthesize tapBlock;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
   tapBlock();
}

- (void)dealloc
{
   [tapBlock release];
   [super dealloc];
}

@end

This is a very convenient way of setting up delegate style behavior without having to litter your code with lots of protocol definitions. Now an example of using the view in a view controller:

- (void)viewDidLoad
{
    [super viewDidLoad];
    tapBlockView = [[TapBlockView alloc] initWithFrame:CGRectZero];
    [tapBlockView setTapBlock:^(void) {
        self.someLabel.text = @"You tapped it!";
    }];
}

Everything seems fine right? Nice and convenient.

Not so fast.

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:

- (void)viewDidLoad
{
    [super viewDidLoad];
    tapBlockView = [[TapBlockView alloc] initWithFrame:CGRectZero];
    __block typeof (self) weakself = self;
    [tapBlockView setTapBlock:^(void) {
        weakself.someLabel.text = @"You tapped it!";
    }];
}

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.

For more information check out these two articles on Mike Ash’s Friday Q&A Site:
9/30/2011 – Automatic Reference Counting
4/30/2010 – Dealing With Retain Cycles

About these ads

Posted on May 11, 2012, in Code, idevblogaday and tagged , . Bookmark the permalink. 11 Comments.

  1. 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).

    Why is __block needed? For non-arc cases would the following work?
    __unsafe_unretained typeof (self) weakself = self;

  2. 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 :-)

  3. I like that BlocksKit has the target object as a parameter to avoid the retain issue.

    [self task:^(id obj){
    //obj == self
    }];

  4. I’m just curious. But even it’s true that the block does retain ‘self’. After that block finishes execution, that ‘self’ would be released and there would be no problem, right?

  5. Another (even nastier) gotcha is using instance variables in the block: it will also retain ‘self’ as it turns out.

    I.e. if you have

    @interface MyClass {
    NSObject *instObj;
    }

    and a block somewhere in implementation of MyClass:

    …. = ^{
    [instObj doSomething];
    };

    self (of MyClass) will be retained even without mentioning ‘self.’
    That took me a while to find and fix: same way with __block or as Apple suggests by using it by value, see documentation:

    Blocks and Variables, Object and Block Variables:

    http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW1

  6. I get pleasure from, cause I found exactly what I used to be having a look for.

    You have ended my 4 day long hunt! God Bless you man. Have a nice day.
    Bye

  1. Pingback: iOS Development Tips for Beginners

  2. Pingback: iOS Development Tips I Would Want If I Was Starting Out Today | What Babak is Reading

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: