Thursday, April 12, 2012

ARC and Leaks: Great But Imperfect - Part 1

This week I spent time debugging yet another memory management bug. The exercise served as a not-so-friendly reminder that correct memory management remains a very difficult task even with technologies such as Automatic Reference Counting (ARC) and the Leaks Instrument.

Hopefully most Objective-C developers understand the main limitation of ARC: ARC can't break retain cycles. It is up to the programmer to prevent or break retain cycles. Luckily the Leaks Instrument is available to help since it can detect retain-cycle-memory-leaks.

With this in mind I generally develop my code in two stages:
  1. Develop the code using ARC and try my best not to introduce retain cycles.
  2. Run the code under the Leaks Instrument to find and break any retain cycles I might have accidentally introduced.

Once you are comfortable with these steps it is easy to gain a false sense of confidence in your memory management handling. We need to remember that Leaks, as good as it is, is not perfect. It does not (and can not) guarantee that it will find all memory leaks.

This week I was reminded of this fact when I noticed what appeared to be a retain-cycle-memory-leak in my code but which was not being reported under Leaks.

Below is a much simplified version of the code. The class is called LeakyViewController, it is a subclass of UIViewController, and it maintains a strong reference to itself which creates a simple retain-cycle.

If you create an instance of this class and immediately throw it away, the instance will never be deallocated and Leaks does not report a problem.

So what is the deal? I'll explore this further in parts 2 and 3 of this blog post series.

@interface LeakyViewController ()
@property (nonatomic, strong) NSObject *selfRef;
@end

@implementation LeakyViewController

@synthesize selfRef;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
NSLog(@"allocating a LeakyViewController");
self.selfRef = self;
}
return self;
}

- (void) dealloc {
NSLog(@"deallocating a LeakyViewController");
}

@end


No comments: