Three Ways To Load A Nib. Only Two Work.

Someone I’m working with told me that you can load a NIB on the iPhone in the following way:

TestViewController *c = [[TestViewController alloc] initWithNibName:@"TestNib" bundle:[NSBundle mainBundle]];
[self presentModalViewController:c animated:YES];

However, this doesn’t appear to work. When chasing down the code, by the time TestViewController -(void)viewDidLoad gets called, self has changed; however, deep inside the lazy load of presentModalViewController the old pointer seems to be used because I get an exception indicating the view reference of the underlying UIViewController (that TestViewController descends from) was uninitialized.

There are two other ways to load the Nib.

First is using -(NSArray *)loadNibNamed:owner:options:

NSArray *a = [[NSBundle mainBundle] loadNibNamed:@"TestNib" owner:self options:nil];
TestViewController *c = [a objectAtIndex:0];
[self presentModalViewController:c animated:YES];

This seems to work, though the top level objects have been offset by one since v2.0 of the iPhone OS. (That is, this code will only work on v2.1 or later; if you build for v2.0 you must add one to the objectAtIndex index.)

The second way is the recommended way given in Apple’s documentation here:

// In my top level class
@interface TestRetainViewController : UIViewController 
    TestViewController *fController;
@property (nonatomic, retain) IBOutlet TestViewController *fController;
[[NSBundle mainBundle] loadNibNamed:@"TestNib" owner:self options:nil];
[self presentModalViewController:fController animated:YES];

This assumes there is a reference in the TestNib.xib file which points from the file owner object to the NIB.

One hitch about the second method, using an array: the array that is returned has a reference count of 1, but also has been set to autorelease. Thus you should either retain the array or the items in the array you want. The hitch about the third method, of course, is that because you’ve declared a property with retain semantics, you must also release the object on dealloc:

- (void)dealloc
    [fController release];
    [super dealloc];

You also can test to see if the NIB was already initialized in the call to loadNibNamed, however, in theory during NIB loading the reassignment to fController should release whatever was already loaded there before.

What tripped me up about the last project I worked on was that I was in such a rush to get very complicated NIBs built for an over-engineered UI, I created circular references and (more importantly) I wasn’t releasing any of the objects loaded via the NIB loading process. Turned out object connections on the iPhone are established using -setValue:forKey:, which does an automatic retain, and that means you need to release the reference in the -dealloc method.

What’s worse, if you don’t explicitly create the @property (or equivalent setXXX call according to the key/value rules, I believe simple assignment with retain is called–which means if you do multiple calls to loadNibNamed: above on TestNib but without the @property declaration, you’ll leak as each call simply overwrites the pointer.

Leave a Reply

Please log in using one of these methods to post your comment: Logo

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

Facebook photo

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

Connecting to %s