Set Delegates to Nil Under Arc

Set delegates to nil under ARC?

Setting non-weak delegates to nil is generally a good idea unless you know you don't have to. For UITableView and UIScrollView, I've experienced crashes on previous iOS versions with the following steps (it may help to run with zombies enabled):

  1. Scroll really fast.
  2. Press Done or the back button or whatever to dismiss the VC.

This appears to happen because the scrolling animation is retaining a reference to the view, so the view outlives the VC. It crashes when sending the scroll event.

I've also seen crashes after dismissing a VC containing a UIWebView while a request is being loaded, where simply setting the delegate to nil was not sufficient (I think the workaround was to call [webView loadRequest:nil]).

Do system object delegates in ARC need to be set to nil?

Yes you should set these delegates to nil.

As suggested by the name, unsafe_unretained references do not retain your view controller so there's no retain cycle or memory leak here. However, unlike weak, these references will not be set to nil automatically when your view controller is deallocated. In most cases this is not a problem as your view controller will outlive its views, or at least be deallocated at the same time.
Unfortunately there are a few cases where UIKit may have also temporarily retained the view. This can allow the view to outlive the view controller and attempt to call delegate methods on the deallocated object resulting in a crash.

The easiest way I know of to see this in action is to dismiss and deallocate a view controller which is a delegate of a scroll view (or one of its subclasses like UITableView) while the scroll is still scrolling (e.g. from a strong swipe gesture over a long list of items). The scroll view will then attempt to call delegate methods (like scrollViewDidScroll) on the deallocated controller.

In dealloc method set any delegate to nil is needed or not needed

If you are deallocating an object that acts as the delegate to other objects, you need to make sure that you have set their delegates to nil, before you call [super dealloc] (assuming the normal pattern that objects do not retain their delegates). This is because when [super dealloc] has returned, this object is no longer a valid object and the objects it is a delegate to effectively have dangling references, if they have not been set to nil.

In this particular case, you would probably get away without doing it because your object's dealloc probably won't get called except when the UI is being dismantled and the table view no longer needs to use its delegate or data source, but don't bet on it.

Setting the modal view controller's delegate to nil with ARC

It shouldn't matter. Your child only uses the delegate to message the parent view controller. Your child view controller won't be making any calls to the parent after it is dealloc'ed, so you won't need to nil the delegate. By using assign or weak you haven't taken an ownership role with respect to the parent, so there is no need to nil the delegate for memory management.

Quick inquiry about delegates and setting them to nil

It depends on the ivar. If Foo owns it “exclusively” and the ivar is not available to other classes, then there is no need to do _ivar.delegate = nil; in dealloc. But if the object is intended to be used in other classes as well, you would better to set the delegate to nil. However, this is very rare situation in Cocoa development.

Another approach mentioned in the answers is to always be safe and set the delegate to nil. But I would not recommend this. Sometimes, by leaving the delegate reference you may find a leaked object which tries to contact its “dead” delegate which owned it.

Why do we set delegate to nil at dealloc if the object is going to be destroyed anyway?

If you hold the only reference to self.tableView, there's no need of setting the delegate to nil.

The only situation where you have to set the delegate to nil, if is another class has your class as a delegate, because if your class is destroyed, that other class will look for your class to implement some methods, and your call won't be there.

Should you set the delegate to nil in the class using the delegate or in the class itself

Yes, you should set the classB's delegate property to nil in classA's dealloc.

It's not a memory management issue, because delegate properties should be marked assign, not retain, to avoid retain cycles (otherwise the dealloc will never be called). The issue is that otherwise classB might message classA after it has been released.

For example, if classB has a delagate call to say "being hidden", and classB is released just after classA, it would message the already dealloc'ed classA causing a crash.

And remember, you can't always guarentee the dealloc order, especial if they are autoreleased.

So yes, nil out the delegate property in classA's dealloc.

Setting delegate to nil in dealloc

It's a defensive programming move. It's clearing out the reference to the delegate object incase something else in your object tries to access the delegate after you've told it that you're done with it. As part of your dealloc you might have a method or do something that triggers a KVO notification that makes a call to the delegate. So setting the delegate's reference to nil prevents that from happening. If it did happen you could end up with some oddball crashes that are fun to reproduce and fix.

ARC delegate memory management

When you have a relationship between the a parent controller and its view, where the parent controller acts as the views delegate, one of those relationships must not retain the other, otherwise you will have a retain cycle and a memory leak.

There are two ways to do this:

  • There first is to mark the delegate as __unsafe_unretained. If you do this you will need to manually nil out the reference in the dealloc of the controller.

  • The second is to use a weak reference. Most of ARC happens at compile time. This helps to save battery drain by reducing the CPU cycles that would otherwise occur with a garbage collector. However, for weak references, there's a run-time process that maintains a map of these variables, observes them, and nils them out as required. This is why weak references required iOS5.1 - its not just a compiler feature.

  • If you use too many weak references, it can be a performance overhead. In practice this will hardly ever be a concern.

Summary

  • No you don't need to manually nil it out if you use weak references. Check that you don't have a memory like via a retain cycle of strong references.
  • Only use __unsafe_unretained (aka 'assign') if you really have to.
  • The same rules apply for UIKit and framework classes. The nice thing is that they're very consistent.

Update

  • Correcting my dodgy comment: If your delegate goes away before the controller does, then you will need to nil it off the controller manually - thanks @borrden.


Related Topics



Leave a reply



Submit