10
votes

I've been reading all day about why views should be set to nil in viewDidUnload and released in dealloc. All the articles keep on repeating the same thing. Yes, I know the behind-the-scene instructions are different, but what are the practical differences?

var = nil

  1. If var is a retained propery, reclaim memory the old object var pointed to.
  2. Set var to nil.

[var release]

  1. Reclaim memory var points to.
  2. var now points to nothing, which is equivalent to nil

To me, both ways of reclaiming memory have the same end result. So why do one over the other? Every book out there tells me to set to nil in viewDidUnload and release in dealloc. Someone should point out the bad things that would happen if a view was released in viewDidUnload and nilled in dealloc.

.h

#import <UIKit/UIKit.h>
@interface DisclosureDetailController : UIViewController {
 UILabel* label;
}
@property (nonatomic, retain) IBOutlet UILabel* label;
@end

.m

#import "DisclosureDetailController.h"
@implementation DisclosureDetailController
@synthesize label;
- (void)viewDidUnload {
 self.label = nil;
 // OR [self.label release];
 [super viewDidUnload];
}
- (void)dealloc {
 [self.label release];
 // OR self.label = nil;
}
2

2 Answers

25
votes

First things first, the line

[self.label release];

is absolutely wrong regardless of where you call it. You should never call -release on the results of property access. This is exactly the same as writing [[self label] release], which I hope you can recognize as being wrong.

Your code sample should look like the following:

- (void)viewDidUnload {
    self.label = nil;
    [super viewDidUnload];
}
- (void)dealloc {
    [label release];
    [super dealloc];
}

If we look at -viewDidUnload first, it's pretty simple. self.label = nil; is correct. Similarly correct would be [self setLabel:nil];. And while not quite as good, it would also be acceptable to write [label release], label = nil;. This last form isn't as good because it bypasses the setter method, which may be doing more things than simply releasing the property (e.g. it may maintain internal state that cares about the value of the property). It also bypasses KVO notifications.

The real question here is what you do in -dealloc. Many people suggest that it's perfectly fine to say self.label = nil;, and practically speaking, this will work most of the time. The problem is, the rest of the time it will cause subtle bugs. There are two things that calling the setter can do. The first is it can cause side effects in your class if the setter method is implemented manually (even if you're not implementing the setter yourself, a subclass might). The second is it can broadcast KVO notifications. Neither of these things are desired when you're in -dealloc. By releasing the ivar directly, as in [label release];, you avoid both the potential side effects and the KVO notifications.

4
votes

the practical differences are as follows.

Setting the property to nil by using the property accessor will let the synthesized method take hold of your new nil property after releasing the existing property.

// we will take for granted that you synthesize this property
@property (nonatomic, retain) IBOutlet UILabel* label;

we will use the property accessor and set it to nil.

//This will in actuality set the new value of nil to the label variable after
//releasing the existing label that it had a retain count on.
self.label = nil; 

next we will release it directly

//This line on the other hand will merely release the label directly. 
//As soon as the label is deallocated you will have a handle to an invalid object. 
//(memory space that used to be your label)
[label release];

now we will show a simplified version of the property accessor. (not to be used literally)

//Simply put the following is an pseudo equivalent of the property setter.
[label release]
label = nil;

the main point here being that the property accessor handles releasing the label that it retained. and setting it to whatever you hand it (in this case being nil)

therefore adding the following code

label = nil;

without releasing the retained object would cause a memory leak and you would have a retain count on a label you no longer have a pointer to.

Note:

another thing to take into account. Any pointer that is nil. will be able to accept messages. And in return they will reply with nil. An object that was released on the other hand, as soon as that memory is deallocated your message to it will most likely throw an error. the outcome is unpredictable. This is a good reason for setting your properties to nil. Not only will it handle the release for the object that it is holding. but it will also give you an object you can safely message to without blowing up.

A good point @WaltSellers

Accessing a variable -- regardless if its the property accessor or instance variable. -- After it has been released completely. Will result in "Undefined" actions. This means that the access may act ok, or it may ruin other parts of the app, or alternatively it may just blow up real fast and terminate the offending app. Basically setting the variable to nil after releasing will enable you to get past that error.

A separate tip from me

to overcome the misconception of property accessor and instance variable I just @synthesize and tell it to set a variable name.

@synthesize label = _label;

doing this allows me to differentiate self.label from its instance variable. as no longer can you access the label variable directly without the preceding _