Blog Archives

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

Advertisements

ARC: Cat’s out of the bag

LLVM has posted about the new automatic reference counting feature coming in (I assume) future versions of the compiler.  I have a hard time thinking of this as a helpful improvement in dealing with memory in objective-c in a non garbage collected environment:

  • It doesn’t change the way you think about your allocations.  You still have to place __weak, __strong, or __autoreleased to tell LLVM how you intend to use the declaration.
  • If you’re already declaring your intentions to the compiler, why not simply go the extra step and follow through.
  • The above also holds for the new @autoreleasepool directive to run a local pool.

I also have a few, more personal reasons for not liking the feature:

  • It makes the code uglier: __weak, __strong are not particularly attractive to look at.
  • Alongside the above, I feel it’s a poor abstraction: In a purely garbage collected environment like java or any scripting language du jour, there’s not even a mental model for memory under the hood with the notable exception of scoping rules.  You can simply “new” an object, and it’s there until you either null it out or it goes out of scope.  In the case of ARC, you still have to have be somewhat aware of what you’re doing, but you don’t necessarily need to REALLY understand what’s going on under the hood in terms of the retain count and balancing retain release calls.  As I see it, a clearer and more complete understanding is better than a foggier one any day.  This brings me to my next point:
  • Understanding and managing your memory makes you a better programmer:  There’s something to be said for the level of detail you must think about while working with the memory model in objective-c, and I can’t help but that it pervades the entire application process from design to testing.
  • Is it really all that hard?  Objective-c provides quite clear ownership rules, and with the reference counting built into NSObject, it’s relatively easy to manage memory properly.  Sure, there are plenty of beginner mistakes to be made both with leaking memory and the dreaded EXEC_BAD_ACCESS crash, but now after writing iOS code for a couple years, typing something like [[NSMutableArray array] retain] feels perfectly natural and automatic.

What are your thoughts on adding ARC to your development workflow?