14
votes

I'm writing iOS apps using ARC and targeting iOS 5+.

Suppose I write a custom view object that has a delegate property. In declaring the delegate property, I make it a weak reference to avoid a retain cycle, so that when the actual delegate object (the controller) is destroyed, my custom view will also be destroyed, as follows:

@interface MyCustomView : UIView

@property (nonatomic, weak) id<MyCustomViewDelegate> delegate;

@end

All is good.

Ok, so now I'm writing the controller object, and it has references to two view objects: my custom view and an Apple-supplied UIKit view, both of which declare delegate properties, and the controller is the delegate for both views. Maybe it looks something like this:

@interface MyViewController : UIViewController <MyCustomViewDelegate, UITableViewDataSource, UITableViewDelegate>

@property (nonatomic, strong) MyCustomView *customView;
@property (nonatomic, strong) UITableView *tableView;

@end

@implementation MyViewController

- (void)viewDidLoad
{
    self.customView.delegate = self;
    self.tableView.dataSource = self;
    self.tableView.delegate = self;
}

@end

My question is this: Do I need to override dealloc to set either or both delegates to nil?

I mean, as I understand it, the delegate property of the UIKit view (in this case, tableView) isn't actually declared to be a weak reference, but rather an __unsafe_unretained reference, for backwards compatibility with non-ARC version of iOS. So maybe I need to write

- (void)dealloc
{
    _tableView.dataSource = nil;
    _tableView.delegate = nil;
}

Now, if I do have to override dealloc, I still don't have to set _customView.delegate = nil, right? Because that was declared (by me) to be a weak reference, so it should be set to nil automatically upon the destruction of MyViewController.

But on the other hand, I'm not targeting non-ARC versions of iOS, nor do I intend to. So maybe I don't need to override dealloc at all?

3
You may not be aware of this, but the current recommendation is that IBOutlets contained in a view hierarchy should be weak. In other words, if tableView is contained in a view, the view is held by strong reference and the tableView doesn't need to be. That makes your case here a bit artificial, though it's still a legitimate question for that case (which has other, more real instances).Steven Fisher
@StevenFisher Thanks, Steven. I was aware of this, but in my example, I didn't declare the view properties to be IBOutlets; perhaps I'm generating them programmatically rather than through IB.Alex Basson
@StevenFisher I guess what I'm most curious about is the difference between '__weak' and '__unsafe_unretained' property declarations, and what my responsibility is to those different declarations when targeting post-ARC versions of iOS.Alex Basson
Whoops! You're right. Apologies. I'm so used to seeing IBOutlet I don't notice when it's not there. It's a good question. :)Steven Fisher

3 Answers

30
votes

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]).

2
votes

If the only strong reference to said tableView is your sole MyViewController controller, you don't need to manually set UITableViewDelegate or UITableViewDataSource to nil.

The reason is that once the dealloc method on your MyViewController is called, the tableview will also be destroyed along with the controller (that is, once again, as long as the only reference to it is your sole controller MyViewController class).

If you have other strong references to this tableview, such as other controllers, it would then be possible that the tableview could then exist longer than the MyViewController class. In such a case, it would be necessary to set the UITableViewDelegate and UITableViewDataSource to nil in the dealloc method of MyViewController because, as you mentioned, these properties are NOT weak references and will not automatically be set to nil.

However, this sort of situation is pretty rare in my experience though.

Most of the time, I don't worry about setting these to nil honestly, but it is a defensive programming practice.

See this post also:

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

0
votes

The only reason you would want to explicitly set the delegate and dataSource to nil is if the customView or the tableView could out live the view controller. Setting them to nil would guard against the delegate or dataSource referencing a deallocated object.

If the customView and tableView will be deallocated along with the view controller, there is no need to nil out the delegate and dataSource.