Do I set properties to nil in dealloc when using ARC?

33,677

Solution 1

Short answer: no, you do not have to nil out properties in dealloc under ARC.

Long answer: You should never nil out properties in dealloc, even in manual memory management.

In MRR, you should release your ivars. Nilling out properties means calling setters, which may invoke code that it shouldn't touch in dealloc (e.g. if your class, or a subclass, overrides the setter). Similarly it may trigger KVO notifications. Releasing the ivar instead avoids these undesired behaviors.

In ARC, the system automatically releases any ivars for you, so if that's all you're doing you don't even have to implement dealloc. However, if you have any non-object ivars that need special handling (e.g. allocated buffers that you need to free()) you still have to deal with those in dealloc.

Furthermore, if you've set yourself as the delegate of any objects, you should un-set that relationship in dealloc (this is the bit about calling [obj setDelegate:nil]). The note about doing this on classes that aren't compiled with ARC is a nod towards weak properties. If the class explicitly marks its delegate property as weak then you don't have to do this, because the nature of weak properties means it'll get nilled out for you. However if the property is marked assign then you should nil it out in your dealloc, otherwise the class is left with a dangling pointer and will likely crash if it tries to message its delegate. Note that this only applies to non-retained relationships, such as delegates.

Solution 2

Just to give the opposite answer...

Short answer: no, you don't have to nil out auto-synthesized properties in dealloc under ARC. And you don't have to use the setter for those in init.

Long answer: You should nil out custom-synthesized properties in dealloc, even under ARC. And you should use the setter for those in init.

The point is your custom-synthesized properties should be safe and symmetrical regarding nullification.

A possible setter for a timer:

-(void)setTimer:(NSTimer *)timer
{
    if (timer == _timer)
        return;

    [timer retain];
    [_timer invalidate];
    [_timer release];
    _timer = timer;
    [_timer fire];
}

A possible setter for a scrollview, tableview, webview, textfield, ...:

-(void)setScrollView:(UIScrollView *)scrollView
{
    if (scrollView == _scrollView)
        return;

    [scrollView retain];
    [_scrollView setDelegate:nil];
    [_scrollView release];
    _scrollView = scrollView;
    [_scrollView setDelegate:self];
}

A possible setter for a KVO property:

-(void)setButton:(UIButton *)button
{
    if (button == _button)
        return;

    [button retain];
    [_button removeObserver:self forKeyPath:@"tintColor"];
    [_button release];
    _button = button;
    [_button addObserver:self forKeyPath:@"tintColor" options:(NSKeyValueObservingOptions)0 context:NULL];
}

Then you don't have to duplicate any code for dealloc, didReceiveMemoryWarning, viewDidUnload, ... and your property can safely be made public. If you were worried about nil out properties in dealloc, then it might be time you check again your setters.

Share:
33,677
emfurry
Author by

emfurry

Updated on June 11, 2020

Comments

  • emfurry
    emfurry almost 4 years

    I am trying to learn Automatic Reference Counting in iOS 5. Now the first part of this question should be easy:

    1. Is it correct that I do NOT need to write explicit release-property statements in my dealloc when using ARC? In other words, is it true that the following does NOT need a explicit dealloc?

      @interface MyClass : NSObject
      @property (strong, nonatomic) NSObject* myProperty;
      @end
      
      @implementation MyClass
      @synthesize myProperty;
      @end
      
    2. My next and more important question comes from a line in the Transitioning to ARC Release Notes document:

      You do not have to (indeed cannot) release instance variables, but you may need to invoke [self setDelegate:nil] on system classes and other code that isn’t compiled using ARC.

      This begs the question: how do I know which system classes are not compiled with ARC? When should I be creating my own dealloc and explicitly setting strongly retaining properties to nil? Should I assume all NS and UI framework classes used in properties require explicit deallocs?

    There is a wealth of information on SO and elsewhere on the practices of releasing a property's backing ivar when using manual reference tracking, but relatively little about this when using ARC.

  • emfurry
    emfurry over 12 years
    This makes sense! Let me ask you this though: a common scenario I have is that I have a MyController : UIViewController class that creates and owns a UIView and also sets the view's delegate to itself. It is the sole retaining owner of that view. When the controller gets dealloc'ed, the view should then also get dealloc'ed. Does it then matter if the delegate pointer is dangling?
  • Lily Ballard
    Lily Ballard over 12 years
    @emfurry: It probably doesn't, because by the time your view controller dies the view itself should not be in the view hierarchy and shouldn't be doing anything, but it's best not to make assumptions. What if the view asynchronously scheduled work to be done later, and the view itself ends up outliving its view controller by a short time (e.g. due to the asynchronous work retaining the view temporarily)? It's best to just nil out the delegate to be safe. And in fact, if the view in question is a UIWebView, the docs explicitly state you need to nil out the delegate.
  • zeiteisen
    zeiteisen about 12 years
    What if I set the delegate property unsafe_unretained? Is this an equivalent to weak and should not be nilled?
  • Lily Ballard
    Lily Ballard about 12 years
    @zeiteisen: No. unsafe_unretained is exactly equivalent to an assign property and is the normal behavior for delegate relationships under MRR, and these need to be nilled out.
  • Sulthan
    Sulthan about 12 years
    I don't agree with statement about not using setters in dealloc with MRC. Apple doesn't recommend it but they do it in their code, too. You can actually create new problems by not using the setter. There are several big discussions about it. What is imporant is writing the setter correctly (it must behave correctly if you pass it a nil value) and sometimes watch the order of deallocation.
  • Lily Ballard
    Lily Ballard about 12 years
    @Sulthan: Whether or not to use setters in dealloc is a huge can of worms, but my position basically boils down to: you want to call as little code as possible in dealloc. Setters have a tendency to include side-effects, either by overriding in subclasses, or via KVO, or other mechanisms. Side-effects in dealloc should especially be avoided like the plague. If you can possibly remove a method call from dealloc, you should do so. This is simplified down to: don't call setters in dealloc.
  • Sulthan
    Sulthan about 12 years
    However, the sideeffect can be needed to release other resources.
  • Lily Ballard
    Lily Ballard about 12 years
    @Sulthan: In the situation where I genuinely need to call methods in dealloc, I isolate that code into a method that, itself, is written to call as little code as possible, and is explicitly documented as "this method is called from -dealloc". I then call this method from the setter and from -dealloc.
  • Sulthan
    Sulthan about 12 years
    My point is that both calling and not calling the setter in dealloc can cause problems. From my experience, when the setter is called and a problem arise, usually you know about the problem immediately (app crashes). When you don't call the setter and it should have released some data, you just have a leak. IMHO it is fine to use both methods if you don't mix them. In general the problems are easy to find and don't appear often with good programming style. In my current project, we have about 300 classes and only in one of them calling or not calling the setters in dealloc makes difference
  • lockedscope
    lockedscope over 11 years
    @Kevin : You said 'this only applies to non-retained relationships, such as delegates.' but this is already obvious from the words in your answers previous sentence; assign and delegates.so what do you want to emphasize with it?
  • Lily Ballard
    Lily Ballard over 11 years
    @lockedscope: The paragraph in general deals with delegate relationships. The final sentence is intended to indicate that this advice applies to any non-retained relationship.
  • Shaun Budhram
    Shaun Budhram over 11 years
    I'd like to add that if you're debugging in iOS5 and you have Zombie Objects enabled, the autosynthesized dealloc method will NOT release memory of strong properties. This behavior is inconsistent with iOS6.
  • Lily Ballard
    Lily Ballard over 11 years
    @ShaunBudhram: What do you mean? If you have zombies enabled, then the memory of any object isn't released. The object is deallocated, and then its class is changed to a dynamically-constructed NSZombie class that throws an exception when messaged.
  • Shaun Budhram
    Shaun Budhram over 11 years
    Released or not, dealloc() is not called on iOS5 if zombies are enabled, however iOS6 does call it. I have verified this in XCode with a simple use-case, on both the simulator and the device.
  • Durai Amuthan.H
    Durai Amuthan.H about 9 years
    one upvote ! If the class explicitly marks its delegate property as weak then you don't have to do this, because the nature of weak properties means it'll get nilled out for you. However if the property is marked assign then you should nil it out in your dealloc
  • Motti Shneor
    Motti Shneor over 8 years
    I accept all but one thing. In MRR I do not agree that the right way is to release _ivars in dealloc - I'd rather nil out the properties because I want KVO observers to know that this property nils out (otherwise they'll just crash a bit later), and I want any overrides and behaviors to happen, and close things down as the properties are released.
  • Lily Ballard
    Lily Ballard over 8 years
    @MottiShneor No, you do not want KVO to trigger in dealloc! That's insanely dangerous! KVO (and people overriding setters in subclasses) is precisely the reason for always avoiding property setters in dealloc.
  • Motti Shneor
    Motti Shneor over 8 years
    Sorry, but I do not agree. dealloc is not "release", and there is no warning from Apple about triggering KVO from there. Apple themselves use property-nullifying technique in their own 'dealloc's, and I can't see a way to make a really CLOSE system, without following the rules to the end. If you rely on KVC/KVO - do it. The only dangerous thing I know related to dealloc and KVO is the other way round. You can not dealloc/release an instance from within a KVO notification if the object is on the chain that called the KVO.
  • Lily Ballard
    Lily Ballard over 8 years
    @MottiShneor This isn't a matter of opinion. Triggering KVO in dealloc, except in very specific and tightly controlled edge cases (which is to say, effectively never), is flat-out wrong. There's lots of reasons why, but the simple answer is that inside of dealloc the invariants that your class exposes are not maintained, and nothing should be interacting with your class when its invariants are broken that doesn't have explicit knowledge about its implementation details. Also, nilling out properties/ivars in dealloc is pointless anyway with ARC.
  • Vishal Singh
    Vishal Singh almost 7 years
    @KevinBallard, is it still not recommended if we assign nil to iVar directly instead of using setter? Doing so, wont call the setter method and, I may be wrong, KVO wont get triggered. (my query is for MRC)
  • Lily Ballard
    Lily Ballard almost 7 years
    @VishalSingh If you're going to nil out properties in dealloc (which you really don't have to), then you definitely should assign to the ivar directly instead of the property (in general, both dealloc and init should access ivars directly instead of properties). This is true for both ARC and MRR.
  • Ayan Sengupta
    Ayan Sengupta almost 4 years
    @LilyBallard what's your point saying "both dealloc and init should access ivars directly instead of properties"? Specifically why do you think we should use ivars in init instead on properties?
  • Lily Ballard
    Lily Ballard almost 4 years
    @AyanSengupta Because while in init the object hasn't been fully initialized yet, but property setters are written with the assumption that the object is initialized. This is especially true if the class is ever subclassed, so don't just assume that because you can see your property setters that it's okay. Swift does the right thing and enforces this; it doesn't invoke property observers, and it requires all stored properties be initialized before you can invoke any code outside of init.
  • Ayan Sengupta
    Ayan Sengupta almost 4 years
    @LilyBallard "in init the object hasn't been fully initialized yet" -- in terms of object lifecycle, no; in terms of memory, yes, with default values set after a successful alloc call. So the object is still in a valid state before init with its ivars set to their default values and calling properties in init merely resembles with manipulating the ivars through some methods which not only is expected some times but is often desirable. Swift doesn't have the concept of ivars and thus shouldn't be comparable. Also what's your concern about subclass? Do you have an example that supports?