views:

43

answers:

3

So, I'm loading by XIB file and it contains a set of UIBarButtonItems. Some of the items are used when the viewDidLoad: is called.

@interface MyViewController : UIViewController  {

  IBOutlet UIBarButtonItem *addButton;
  IBOutlet UIBarButtonItem *editButton;
  IBOutlet UIBarButtonItem *doneButton;
}

// NB: There are no properties retaining anything.

@end

@implementation MyViewController

- (void)viewDidLoad {

  [super viewDidLoad];

  NSArray *initialToolbarItems = 
    [[NSArray alloc] initWithObjects: addButton, editButton, nil];

  self.toolbarItems = initialToolbarItems;
  [initialToolbarItems release];
}

- (void)dealloc {
  [super dealloc];

  // Nothing else to do here since we are not retaining anything.
  // … or are we? <insert dramatic music here>
}

@end

If I push the above the above ViewController onto a UINavigationController everything seems fine, all the IBOutlets are assigned and behave like expected.

The instant i pop the ViewController from the navigation stack Instruments' Leaks tells me that I am leaking a UIBarButtonItem. Woe is me!

If I change dealloc: to

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

no leaks occur. The same goes if I use doneButton in viewDidLoad:

  NSArray *initialToolbarItems = 
    [[NSArray alloc] initWithObjects: addButton, editButton, doneButton, nil];

My question: Why is my IBOutlet leaking when I don't use it. I don't retain it at any point. The the NIB loader should own the object, right?

A: 

If you have @property with (retain) declared for the your IBOOutlets they will be retained and must be released

willcodejavaforfood
I don's have a property retaining the it. The code is as you see it above.
Thomas Børlum
A: 

The array retains them

tadej5553
OK, I'm not 100% sure about that. What property do you have for toolbarItems? I think you have retain. Just add [toolbarItems release] in dealloc
tadej5553
Indeed it does, but the toolbarItems array is a property of UIViewController. It is taken care of by the call to [super dealloc], and he releases the array he creates.
Jergason
Yes it does, but when the `toolbarItems` property is released by `[super dealloc]`, `release:` called for each of the arrays children. In any case this does not explain why it is only when the `IBOutlet` is unused the problem occurs.
Thomas Børlum
+2  A: 

Only thing I can think of:

The nib loader treats IBOutlets as strong references. All outlets are retained by default unless you specifically indicate assign. So you still need to release them in dealloc and viewDidUnload.

You can also use a assigned property to make it a weak reference:

@property (nonatomic, assign) IBOutlet UIBarButtonItem *doneButton;

Some reading: http://weblog.bignerdranch.com/?p=95

Rengers
Adding a property with `assign` does remove the leak. Thanks! This does, on the other hand not explain why the outlet is released correctly when I actually use it, e.g. like in the last code above. This would suggest that UINibLoading only releases its' retained outlets the instant they are retained by something else! How convoluted! I haven't gotten around to read the whole article you linked to, but you seem to have hit the nail right on the head! How cruel of apple to force me to write properties when I don't want to. :.(
Thomas Børlum
_An update for anyone having the same problem._ There are two solutions to the problem.1# Always either make properties for with `retain` every outlet. And set them to `nil` in `-viewDidUnload:` and release the ivar in `-dealloc:`. If you set the property to `assign` you are going to have to make sure the outlet is loaded whenever you want to use it!#2 Or, as I do, and don't make properties for outlets unless you really need them. Then in `-viewDidUnload:` do `[myOutlet release], myOutlet = nil;` and in `-dealloc:` do `[myOutlet release]`. In the end this will be less code.
Thomas Børlum