10

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;
}
JoJo
  • 19,587
  • 34
  • 106
  • 162

2 Answers2

25

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.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • Can you explain what happens if I choose to release in `viewDidUnload` instead of setting to nil? What are these side effects you speak of when using setters in `dealloc`? And why do you not want KVO notifications? AND... `self` is a `@property`? How would I refer to a instance variable like `this.foo` in Java? I know `this` is not required, but it makes the code clearer. – JoJo Apr 20 '11 at 22:44
  • @JoJo: If you release in `-viewDidUnload` but don't set to nil, you'll end up crashing the next time you access this ivar (which will happen in any access to the property, whether getter or setter, and in `-dealloc` when you try and release the property). – Lily Ballard Apr 20 '11 at 22:48
  • 3
    @JoJo: Side effects are anything and everything. Setters are just methods. The compiler synthesizes setters for you, but you're free to implement them yourself simply by writing a method named (by default) `-setPropertyName:`. In this setter you can do whatever you want, including performing actions that are not suitable for doing inside of -dealloc, such as retaining self. Similarly, KVO notifications are a bad idea inside of -dealloc. That one should be obvious. Your object is going away - nobody else should get a blow-by-blow account of you tearing down your ivars. – Lily Ballard Apr 20 '11 at 22:50
  • 1
    @JoJo: I don't understand your comment about `self` being a `@property`. That concept doesn't make any sense. Properties belong to objects. If `self` was a property, what object would it belong to? `self` is actually just an implicit parameter to the method call that the Obj-C compiler inserts. You can refer to ivars using `self->ivarName`, which is functionally identical to just using `ivarName`. – Lily Ballard Apr 20 '11 at 22:51
  • @Kevin Ballard: When you said `[self.label release]` is equivalent to `[[self label] release]`, this implied that `self` has a getter defined. Well, I did not define a getter for `self`. The only way to get a getter without writing one myself is to make it a `property`. So that's why I thought `self` was a `property`. – JoJo Apr 20 '11 at 22:57
  • @JoJo: What do you mean, `self` has a getter defined? `self` is the target of the method. If I said `[self self]`, then that would be mean there's a getter named `-self`, but I didn't. If you say `id foo = ...; [foo label];` do you think this means there's a getter named `foo`? In the case of `[[self label] release]`, `self` is the original target of the message `-label`, which invokes the getter and returns your label object. This object is then the target of the message `-release`, which invokes the release method and releases an object that shouldn't be released. – Lily Ballard Apr 20 '11 at 23:01
  • @Kevin Ballard: Are you saying that a pointer to a deallocated object is not the same to a pointer to `nil`? If I haphazardly use a pointer to `nil`, the program will silently proceed? And If I use a pointer to a deallocated object, the program will crash? If I were to dealloc without nilling in `viewDidUnload`, when will the program use that pointer again, which you say will crash the program? I thought if the view is off the screen, it shouldn't be used anymore. – JoJo Apr 20 '11 at 23:05
  • @Kevin Ballard: I'm still confused at when to use `self.label` VS `label` VS `self->label`. You said `[self.label release]` is equivalent to `[[self label] release]`. Applying the same principle, your `self.label = nil` would be the same as `[self label] = nil`. This would set the label object to nil, not the pointer to the object. – JoJo Apr 20 '11 at 23:13
  • 1
    @JoJo Actually, `self.label = nil` would be equivalent to `[self setLabel:nil]` in most cases. A dotted property reference as an lvar is different from a dotted property reference as an rvar. The names of the corresponding getter and setter methods are in fact configurable by the `getter=` and `setter=` options of the `@property` directive. – Jeremy W. Sherman Apr 20 '11 at 23:19
  • @Jeremy W. Sherman: can I do `self->label = nil` or `label = nil`? – JoJo Apr 20 '11 at 23:28
  • 3
    @JoJo: Yes, a pointer to a deallocated object is very different than one to nil. The former is a pointer to garbage memory, and you will almost certainly crash if you attempt to do anything with it (such as calling `-release` on it). A pointer to nil, on the other hand, is safe. It will silently ignore any message sent to it. Saying `id foo = nil; [foo doSomething];` will just skip the call to `-doSomething` entirely. If you release an obj in `-viewDidUnload`, but don't nil it out, you'll crash when you try to release it again in `-dealloc`, or if you use `self.foo = somethingElse`. – Lily Ballard Apr 20 '11 at 23:35
  • 1
    @JoJo: In general, use `self.label` everywhere outside of `-init`, `-dealloc`, and a custom getter/setter. In those situations, use `label`. The `self->label` construct is really only useful inside of `-copyWithZone:` or, in some situations, inside blocks (typically properties will be used here though) and is considered bad form elsewhere. – Lily Ballard Apr 20 '11 at 23:36
  • 1
    @JoJo: If you say `label = nil` or `self->label = nil` (both perform identically), you bypass any setter on the property, and you also bypass the memory management semantics of the property. In other words, this will avoid releasing the old value stored in `label`. In `-dealloc`, you want to say `[label release]`, and then optionally `label = nil` afterwards. Everywhere else you probably want `label = nil`. – Lily Ballard Apr 20 '11 at 23:38
  • @Kevin Ballard: I see now. The dot notation either calls the getter or setter of that property. Why do I see people use `[self.ivar method]` rather than `[self->ivar method]` or `[ivar method]`? Is the compiler smart enough to convert `.` into `->` to avoid the extra memory usage of calling the getter of the ivar? – JoJo Apr 21 '11 at 00:28
  • 2
    @JoJo: `.` never gets converted to `->`. That makes no sense at all. People say `[self.property method]` because, in general, property access is preferred over direct ivar access if a property exist. By sticking to property access wherever you can, you make it easier to obey the memory management semantics enforced by the property, you get atomic access if the property is marked as such, and your code is more flexible because the getter/setter can be overridden by a subclass, or even by yourself later in the development of the class. – Lily Ballard Apr 21 '11 at 00:34
  • 1
    @JoJo: the important thing to bear in mind is that while most properties have synthesized accessors and their getters are mostly functionally equivalent to accessing the ivar directly (with some caveats), that's not always the case. I could write a property today, and then in a subclass or later down the line re-implement the getter to do something different. For example I could lazily initialize the property in the getter. If I used ivar access, this wouldn't be possible, but property access allows me to make these changes later. – Lily Ballard Apr 21 '11 at 00:36
  • Just a note -- using ARC you can no longer release ivars -- just set the property to nil. In addition, `viewDidUnload` is depreciated in iOS6 and no longer called. As a result, properties should be nilled in `-dealloc`. – memmons Oct 24 '12 at 15:31
  • @Answerbot: I disagree. You can't release ivars, but you can certainly nil them. However, ARC will nil your ivars automatically, so if you're using ARC you should just not touch any of the properties/ivars in dealloc. In fact, you may not even need dealloc at all. – Lily Ballard Oct 24 '12 at 19:14
4

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 _

The Lazy Coder
  • 11,560
  • 4
  • 51
  • 69
  • You're conflating "property", "ivar", and "object" in your comment about `[label release]`. – Lily Ballard Apr 20 '11 at 22:53
  • 1
    Sometimes I use the terms interchangeably. I admit it is a bad practice. I will try to demonstrate the differences better in the future. Thank you for your criticism. – The Lazy Coder Apr 20 '11 at 23:01
  • Suggestion for expressing the note above: Remember that when an object is released, the memory is deallocated, but the address to the memory is still in your member variable. It is now a dangling pointer. You can prevent the related exceptions and crashes by setting the dangling pointer to nil. – Walt Sellers Apr 02 '13 at 21:23
  • A good point, I added your comment into the post. I have not been to this post in a while. So I expanded on the post while I was at it. – The Lazy Coder Apr 03 '13 at 03:33