在ARC下将代理设置为nil?

14
我正在使用ARC编写iOS应用程序,目标版本为iOS 5+。
假设我编写了一个自定义视图对象,该对象具有委托属性。在声明委托属性时,我将其设置为弱引用以避免保留循环,这样当实际的委托对象(控制器)被销毁时,我的自定义视图也会被销毁,如下所示:
@interface MyCustomView : UIView

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

@end

一切都很好。

好的,现在我正在编写控制器对象,它引用了两个视图对象:我的自定义视图和一个由苹果提供的UIKit视图,两者都声明了委托属性,并且控制器是这两个视图的委托。也许它看起来像这样:

@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

我的问题是:我是否需要重写dealloc方法来将两个代理都设置为nil?
我的意思是,据我所了解,UIKit视图(在这种情况下为tableView)的delegate属性实际上并没有声明为弱引用,而是一个__unsafe_unretained引用,以便与非ARC版本的iOS向后兼容。因此,也许我需要编写
- (void)dealloc
{
    _tableView.dataSource = nil;
    _tableView.delegate = nil;
}

现在,如果我必须要重写dealloc方法,那么我是不是还需要设置_customView.delegate = nil呢?因为它被我声明为一个弱引用,所以在MyViewController销毁时应该自动设置为nil。

但另一方面,我并不是针对非ARC版本的iOS,也没有这个意图。所以也许我根本不需要重写dealloc方法?


你可能不知道,但目前的建议是,包含在视图层次结构中的IBOutlets应该是弱引用。换句话说,如果tableView包含在一个视图中,那么视图会被强引用,而tableView则不需要。这使得你在这里的情况有点人为,尽管它仍然是一个合法的问题(它还有其他更真实的实例)。 - Steven Fisher
@StevenFisher 谢谢,Steven。我知道这一点,但在我的例子中,我没有声明视图属性为IBOutlets;也许我是通过编程而不是通过IB来生成它们的。 - Alex Basson
@StevenFisher 我最好奇的是 '__weak' 和 '__unsafe_unretained' 属性声明之间的区别,以及在针对后ARC版本的iOS时,我对这些不同声明的责任是什么。 - Alex Basson
哎呀!你说得对。抱歉。我太习惯看到IBOutlet了,没注意到它不在那里。这是个好问题。 :) - Steven Fisher
3个回答

30

将非弱委托设置为nil是一个好主意,除非你确定不需要。对于UITableViewUIScrollView,我曾在以前的iOS版本中经历过崩溃,具体步骤如下(启用僵尸对象调试可能会有所帮助):

  1. 快速滚动。
  2. 按“完成”或返回按钮或其他按钮来关闭视图控制器。

这似乎是因为滚动动画保留了对该视图的引用,因此该视图超出了视图控制器的生命周期。当向滚动事件发送消息时,它会导致崩溃。

当正在加载请求时,关闭包含UIWebView的视图控制器后,我也曾看到过崩溃情况,仅将委托设置为nil是不够的(我认为解决方法是调用[webView loadRequest:nil])。


你发现这种情况只出现在没有使用UITableViewController的情况下吗?像OP一样? - Bob Spryn
@BobSpryn 我不知道,我没有记录(但通常我使用自定义VC,因为UITableViewController不太灵活)。完全有可能UITableViewController-dealloc中取消设置表视图的数据源/委托。 - tc.

2
如果对于表格视图tableView,唯一的强引用是您的唯一控制器MyViewController,则不需要手动将UITableViewDelegateUITableViewDataSource设置为nil
原因是一旦调用了MyViewController上的dealloc方法,表格视图也将随着控制器一起被销毁(也就是说,只要对它的唯一引用是您的唯一控制器MyViewController类)。
如果您对此表格视图有其他强引用,例如其他控制器,则该表格视图可能会比MyViewController类存在更长的时间。在这种情况下,需要在MyViewControllerdealloc方法中将UITableViewDelegateUITableViewDataSource设置为nil,因为正如您所提到的,这些属性不是弱引用,不会自动设置为nil
但是,根据我的经验,这种情况非常少见。
大多数时候,老实说,我不担心将它们设置为nil,但这是一种防御性编程实践。
请参见此帖子: 在dealloc方法中是否需要将任何委托设置为nil

我们有一个只有几百个用户的小型应用程序,我每天看到3-4次与此相关的崩溃。堆栈中涉及CoreAnimation,因此我怀疑动画在其控制器(delegate和dataSource)被释放后仍然保持对表的强引用。但是,我还没有成功地复现这种情况。如果您正在使用UITableViewController并且将delegate和dataSource设置为UITableViewController,则可能不会出现此问题。在这种情况下,Apple会在dealloc方法中清除它们。 - Steve

0

如果customViewtableView的生命周期可能超出视图控制器,则唯一需要显式设置delegatedataSourcenil的原因是。将它们设置为nil可以防止delegatedataSource引用一个已释放的对象。

如果customViewtableView会随着视图控制器一起被释放,则无需将delegatedataSource设置为nil


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接