17

I have a .xib file containing a UIView and 2 UILabel subviews linked to a class named Note with outlets assigned to each label appropriately, the definition for this class contains the following.

@interface Note : UIView {
    IBOutlet UILabel *time;
    IBOutlet UILabel *content;
}

I'm constructing this with the following code

NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"Note" owner:self options:nil];
note = [nibViews lastObject];
[self addSubview:note];

Now, in my Note class dealloc phase, I'm not releasing either time or content, but I'm wondering if I should?

- (void)dealloc {
    [super dealloc];
}

I'm assuming I don't because I'm not explicitly retaining these objects anywhere in my code, and I don't synthesize these into getter/setters. But I don't know enough about nib unarchiving to know whether I should be releasing these in my dealloc phase or not?

seanalltogether
  • 3,542
  • 3
  • 26
  • 24

5 Answers5

14

You should release an IBOutlet even if you're not writing or synthesizing accessor methods. The NIB lifecycle documentation says that although unarchived objects are initially set to autorelease, the retain count on them is bumped up by an extra 1 when UIKit hooks up all the IBOutlet connection bindings. You therefore need to manually decrement via release when you're done.

It's not obvious that UIKit would be doing this so you might assume you can just leave the setter/getter methods off and trust that everything is autoreleased. But that's not the case.

Notice that Interface Builder templates explicitly release any IBOutlets, so any you add should be treated likewise.

pioneer78
  • 683
  • 5
  • 11
2

This is correct for the iPhone; it would not be correct on the Mac, though.

However, you may want to rework this code. It isn't safe to assume that the view will be the last object loaded from the nib. I'd suggest instead that you either connect it to a "note" outlet in your view controller or scan the list for an object that's a subclass of Note. (If you're loading multiple Notes and you use the outlet option, just make sure you add one Note before loading another.)

Becca Royal-Gordon
  • 17,541
  • 7
  • 56
  • 91
  • What do you mean I can't guarantee it will be the last object? Do you mean because its possible to order the top level of a nib in any order you want, or that at the time of unarchiving the order can be changed from what appears in interface builder? – seanalltogether Dec 20 '08 at 03:34
  • 4
    The order when unarchiving is not guaranteed. For example, between iPhone OS 2.1 and 2.2 a single UITableViewCell placed in a xib when loaded was returned in different positions within the array (even with only a single object there is more than one element in the array returned by loadNibNamed). – Kendall Helmstetter Gelner Dec 30 '08 at 23:59
0

You will not be able to take them out of memory unless you declare the IBOutlets as properties and synthesize them to ivars:

@interface Note : UIView {
    IBOutlet UILabel *time;
    IBOutlet UILabel *content;
}

@property (nonatomic, retain) IBOutlet UILabel *time;
@property (nonatomic, retain) IBOutlet UILabel *content;

@end


@implementation Note
@synthesize time, content;

// your methods

- (void)dealloc {
     [time release], time = nil;
     [content release], content = nil;
     [super dealloc];
}

@end
Nate Symer
  • 2,185
  • 1
  • 20
  • 27
0

You DO need to release it. See

"As it rebuilds the object hierarchy, however, UIKit reestablishes connections between the objects using the setValue:forKey: method, which uses the available setter method or retains the object by default if no setter method is available." in Nib Object Retention

In other words, because you haven't specified these entries as @property (which implicitly declares a setter), or provided a setter directly, there is no available setter method, and the final part of this paragraph applies - the object is retained by default.

You should, in your dealloc() method, set all IBOutlets to nil using

self.outletName = nil;

If no setter is defined, then setValue will autorelease the old value (make sure you have an NSAutoreleasePool if running in a thread). If a setter is defined, it will perform whatever behaviour you defined. Either way, the set to nil will do exactly the right thing, and ensure you get no memory leaks. Do NOT do this

outletName = nil;

This will directly set the member variable, and bypass calling setValue.

See the documentation of NSObject setValue:forKey for more details.

Running the Performance Tool (Leaks) will not show a leak, but you can verify that there is actually a leak by looking at the current running total of allocated memory.

cf The Airsource - Memory Management and NIBs

Airsource Ltd
  • 32,379
  • 13
  • 71
  • 75
  • self.outletName = nil; No, do *not* do this; you should not use accessor methods in dealloc. – mmalc Jan 21 '09 at 02:54
  • Re "In other words, because you haven't specified these entries as @property": declaring a property has nothing to do with it. It's the presence or absence of the accessors themselves that's important, not the syntactic sugar. – mmalc Jan 21 '09 at 02:56
  • Agreed, and edited. My point was that he hadn't declared accessors even by the hidden method of doing it as an @property, but that didn't come across to well. – Airsource Ltd Jan 22 '09 at 18:07
  • Regarding accessor methods, you made this point on my blog as well, and I still disagree. – Airsource Ltd Jan 22 '09 at 18:09
-6

I belive you are correct. See the documentation links below for more details.

Documentation:

itsaboutcode
  • 24,525
  • 45
  • 110
  • 156
bjartek
  • 929
  • 1
  • 5
  • 11
  • the answer is incorrect as explained in the other posts of this thread and as documented by Apple. – amok Jan 01 '11 at 03:11
  • 1
    This answer is correct for Mac OS X, but not for iOS. Take a look at this link under "Top-Level Objects". http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmNibObjects.html#//apple_ref/doc/uid/TP40004998-SW2 – Jesse Rusak Jan 15 '11 at 14:34