views:

300

answers:

2

Can someone please school me on the proper way to load a view hierarchy from a nib file. I am using the loaded view as a template to stamp out a family of views and the current approach I am using is subtly broken. I don't appear to be copy-ing or retain-ing when I should be. Here's the relevant code:

// pageSet is a list of view tag numbers I'll be using
for (NSNumber *n in pageSet) {

    NSUInteger viewTag = [n integerValue];

    // Ingest the nib. Should there be a copy or retain here?
    NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"RandomStripe" owner:self options:nil];

    // Pull the view from the nib. Should there be a copy or retain here?
    MyView *view = (MyView *)[topLevelObjects objectAtIndex:0];

    // The view has a label as it's subview
    UILabel *pageNumberLabel = [view.subviews objectAtIndex:0];
    pageNumberLabel.text     = [NSString stringWithFormat:@"%d", viewTag];

    CGFloat xOffset = ((float) viewTag) * self.scrollView.bounds.size.width;

    view.frame = CGRectMake(xOffset, 0, self.scrollView.bounds.size.width, self.scrollView.bounds.size.height);
    view.tag = viewTag;

    // Insert the view as a child of my containerView
    [self.containerView addSubview:view];

} // for (pageSet)

This has be making my head hurt for while now?

Cheers, Doug

+2  A: 

If you use IBOutlets from within Interface Builder back to your code, things would be a little easier. That way as soon as you try to access the UIView outlet you have set up, it gets loaded, with all its children, and then the UIView's initWithCoder will get called (useful if you have subclassed it).

@property (retain, nonatomic) IBOutlet iiView   *iiView;

Otherwise, I do this:

if (self.numberView == nil) {
 NumberView *numView = [[NumberView alloc] initWithNibName:@"NumberView" bundle:[NSBundle mainBundle]];
 self.numberView = numView;
 [numView release];
}

(With an IBOutlet for numberView, I just go ahead and start using numberView instead of the code above)

In both conditions, all the subviews - the children - of numberView will get loaded at the same time. If I needed to access a label or button I would do an IBOutlet for those too so I won't have to traverse the view hierarchy looking for them.

IBOutlet UIButton *nextButton,
    *stopButton1,
    *stopButton2,
    *infoButton,
    *bitsonthegoButton;
IBOutlet UILabel *pointsLabel1,
   *pointsLabel2,
   *totalPointsLabel1,
   *totalPointsLabel2;
mahboudz
A: 

I wasn't sure if the other answer was exactly clear on this point, but any IBOutlets you set up on your view will get wired in with that same loadNibNamed: call you are doing today. So in the header for that view, you'd declare IBOutlets for both myView and pageNumberLabel, then attach them to the File's Owner in the xib (and set File's Owner type to be the view you are calling loadNibNamed: from).

That's just to make your life easier. To answer the question you asked, the documentation for "loadNibNamed:owner:options" tells us:

You should retain either the returned array or the objects it contains manually to prevent the nib file objects from being released prematurely.

So the array is autoreleased, and all the objects in it are as well (which you would expect from a call returning an autoreleased array). Since you want to keep the view you would retain that - doing so would mean any subviews of the main view would be kept around as well, since a view will retain anything set as a subview. If you also retained the label that would cause a leak when you released the main view unless you also release the label at the same time (but there seems no point in doing that).

However note that when I say "retain the view", you are already doing so in the code you posted, simply by adding it as a subview to your container view - as noted the view retains subviews. MyView will be released if it's ever removed from the subview, so you'd want to retain it if you do that for any reason and want it kept around.

Nib loading memory management seems complex at first, but the rules are actually much simpler than it would seem.

Kendall Helmstetter Gelner
Kendall,I've re-read the docs on this closely - Resource Programming Guide - and as far as I can tell. I should be completely in the clear with this code. It should work correctly. Each iteration threw the loop should initiate an new fresh read of the nib file. Since I am doing no alloc/copies I should be good to go. As "The Rules" state.I remain unclear on why this simple code doesn't work as intended.
dugla
Actually, I agree above the code as is should work - how does it break?
Kendall Helmstetter Gelner