May 31, 2009

Malloc Misunderstandings

I have been pulling my hair out lately with iPhone development memory management techniques in Objective-C, and I thought I'd share one of my recent understandings.


I had a nib-based view controller that I wanted to use as a tableHeaderView for my UITableViewController. I created the classes for my custom view (we'll call it CustomHeaderViewController) and imported them into my UITableViewController subclass. I want to manipulate the IBOutlets of the CustomHeaderViewController, so I wanted to keep a reference to the class. For this I'll auto-synthesize getters and setters for retaining.

Here's my CustomTableView.h at this point:

#import "CustomHeaderViewController.h"


@interface CustomTableView : UITableViewController {

CustomHeaderViewController *tableHeader;

}


@property (nonatomic, retain) CustomHeaderViewController *tableHeader;


In my implementation of the CustomTableView, I put in the proper @synthesize code and make sure I'm releasing tableHeader in my dealloc method. So far, so good (and so simple).

@implementation CustomTableView


@synthesize tableHeader;


- (void)viewDidLoad {

[super viewDidLoad];


// initialize my table header here.

}


- (void)dealloc {

[tableHeader release];

tableHeader = nil;

[super dealloc];

}


Here's where my mistake began. I know that the UITableView has a setTableHeaderView method, so I used it, directly allocating my new CustomHeaderViewController as I did it.


- (void)viewDidLoad {

[super viewDidLoad];


self.tableHeader = [[CompanyTableViewHeader alloc] init];

[self.tableView setTableHeaderView: tableHeader.view];

}


My project builds correctly, and everything seems to work fine, but there's a memory leak. To see this, let's look at the retain counts in my code now:


- (void)viewDidLoad {

[super viewDidLoad];


// [tableHeader retainCount] == 0


self.tableHeader = [[CustomHeaderViewController alloc] init];


// [tableHeader retainCount] == 2


[self.tableView setTableHeaderView: tableHeader.view];


// [tableHeader retainCount] == 2

}

- (void)dealloc {

// [tableHeader retainCount] == 2


[tableHeader release];


// [tableHeader retainCount] == 1


tableHeader = nil;

[super dealloc];

}


So when my UITableViewController is deallocated, the CustomHeaderViewController instance still has a retain count of 1. The problem lied in the line where I first allocated the instance (creating a retainCount of 1), and immediately assigned it to the to the tableHeader pointer (bumping the retainCount to 2). I can do two things to remedy this. First, I could change my tableHeader setter to only assign instead of retain:


@property (nonatomic, assign) CustomHeaderViewController *tableHeader;


The preferred way, however, would be to allocate the CustomHeaderViewController to a temporary pointer, then release it when done.


- (void)viewDidLoad {

[super viewDidLoad];


CustomHeaderViewController *header = [[CustomHeaderViewController alloc] init];

self.tableHeader = header;

[header release];

[self.tableView setTableHeaderView: tableHeader.view];

}


There you go. I hope this post saves someone the hour of debugging I've already lost!


P.S.: I apologize for the styles of the code snippets, I have never posted code on this blog before. If you have recommendations on how to improve formatting when copy-pasting from Xcode to the Blogger WYSIWYG editor, let me know!

[[Updates alloc] init]

I haven't updated this blog in a long time, as I've been so busy with other projects. Since I'm no longer halfway around the world from many of those who would care to hear about my day-to-days, and I have fewer causes to champion since Barack Obama became Mr. President, I'm not sure where this blog quite fits in.

Here's what I'm up to now:

Though it's been moving slower than I'd prefer, brewmance.com is wrapping up alpha development. The founding purpose of the site is to help cultivate a relationship between casual beer drinkers and beers worth drinking. There is such a rich, rapidly growing selection of micro- and craft brews in the United States but the interested audience is still small. While we can't change the fact that most bars only carry mediocre (and now foreign owned) big brands, we can encourage users to seek out a selection of beers they can fall in love with.
Our access barrier is intentionally low: answer 5 questions about your tastes and personality and we'll return you 5 beers from our proprietary engine. You can then optionally register for a Brewmance profile (using your Google Account) and save your tasting queue for future access. Having a profile makes it easy to review beers you've tried and receive additional recommendations based on your feedback.
Head over to brewmance.com now to sign up for a notification when our alpha preview is ready for testing.

I've also been auditing the iPhone Application Programming (CS193P) course at Stanford through iTunes U. My very limited history in C development has helped, but overall it's been a huge learning curve to overcome. I can't stress how rewarding it's been so far, though. Assuming the demand inertia for native applications keeps growing, these will be valuable skills to keep up.

Another wedding season is here, and I've got some real exciting and beautiful events on my plate for this summer. In addition, we're evangelizing our services on our blog and Facebook.

So I'll try to keep this space updated with updates on the above 3 projects and whatever new comes by. If you're still subscribed, or happened to browse through, thanks for reading!