【问题标题】:Set delegates to nil under ARC?在 ARC 下将代表设置为零?
【发布时间】:2013-02-22 12:11:45
【问题描述】:

我正在使用 ARC 编写 iOS 应用程序并针对 iOS 5+。

假设我编写了一个具有委托属性的自定义视图对象。在声明delegate属性时,我将其设为弱引用以避免retain循环,这样当实际的delegate对象(控制器)被销毁时,我的自定义视图也将被销毁,如下:

@interface MyCustomView : UIView

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

@end

一切都好。

好的,现在我正在编写控制器对象,它引用了两个视图对象:我的自定义视图和 Apple 提供的 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)实际上并未声明为弱引用,而是声明为 __unsafe_unretained 引用,以便向后兼容非 ARC 版本的 iOS。所以也许我需要写

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

现在,如果我必须重写 dealloc,我仍然不必设置 _customView.delegate = nil,对吗?因为这被(我)声明为弱引用,所以它应该在 MyViewController 销毁时自动设置为 nil。

但另一方面,我不针对非 ARC 版本的 iOS,也不打算这样做。所以也许我根本不需要覆盖 dealloc?

【问题讨论】:

  • 您可能没有意识到这一点,但目前的建议是包含在视图层次结构中 的 IBOutlets 应该是弱的。换句话说,如果 tableView 包含在视图中,则视图由强引用持有,而 tableView 不需要。这使您的案例有点人为,尽管对于该案例(还有其他更真实的实例)仍然是一个合理的问题。
  • @StevenFisher 谢谢,史蒂文。我知道这一点,但在我的示例中,我没有将视图属性声明为 IBOutlets;也许我是以编程方式而不是通过 IB 生成它们。
  • @StevenFisher 我想我最好奇的是 '__weak' 和 '__unsafe_unretained' 属性声明之间的区别,以及针对 iOS 的后 ARC 版本时我对这些不同声明的责任是什么.
  • 哎呀!你是对的。道歉。我已经习惯了看到 IBOutlet 我没有注意到它什么时候不存在。这是个好问题。 :)

标签: ios delegates automatic-ref-counting


【解决方案1】:

将非弱委托设置为 nil 通常是一个好主意,除非您知道您不必这样做。对于 UITableViewUIScrollView,我在以前的 iOS 版本上遇到过崩溃,执行以下步骤(它可能有助于在启用僵尸的情况下运行):

  1. 滚动速度非常快。
  2. 按完成或后退按钮或其他任何方式关闭 VC。

这似乎是因为滚动动画保留了对视图的引用,因此视图的寿命比 VC 长。发送滚动事件时崩溃。

我还看到在加载请求时关闭包含 UIWebView 的 VC 后崩溃,其中仅将委托设置为 nil 是不够的(我认为解决方法是调用 [webView loadRequest:nil])。

【讨论】:

  • 您是否仅在没有像 OP 那样使用 UITableViewController 的情况下发现了这种情况?
  • @BobSpryn 我不知道;我没有跟踪(但通常我使用自定义 VC,因为 UITableViewController 不够灵活)。 UITableViewController 完全有可能在-dealloc 中取消设置表格视图的数据源/委托。
【解决方案2】:

如果对上述tableView 的唯一strong引用是您唯一的MyViewController 控制器,则无需手动将UITableViewDelegateUITableViewDataSource 设置为nil

原因是,一旦你的MyViewController 上的dealloc 方法被调用,tableview 也将与控制器一起被销毁(也就是说,再一次,只要对它的唯一引用是你唯一的控制器MyViewController 类)。

如果您对此 tableview 有其他强引用,例如其他控制器,则 tableview 可能存在的时间比MyViewController 类更长。在这种情况下,有必要在 MyViewController 的 dealloc 方法中将 UITableViewDelegateUITableViewDataSource 设置为 nil,因为正如您所提到的,这些属性不是弱引用,不会自动设置为nil

不过,根据我的经验,这种情况非常少见。

大多数时候,老实说,我并不担心将这些设置为 nil,但这是一种防御性编程实践。

另请参阅此帖子:

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

【讨论】:

  • 我们有一个只有几百个用户的小应用程序,我每天看到 3-4 次与此相关的崩溃。 CoreAnimation 在堆栈中,所以我怀疑动画在其控制器(委托和数据源)被释放后持有对表的强引用。但是,我还没有设法重现这一点。如果您使用 UITableViewController 并且委托和数据源设置为 UITableViewController,这可能不是问题。在这种情况下,Apple 在 dealloc 方法中将它们清除。
【解决方案3】:

您希望将delegatedataSource 显式设置为nil 的唯一原因是customViewtableView 是否可以在视图控制器中运行。将它们设置为 nil 可以防止 delegatedataSource 引用已释放的对象。

如果customViewtableView 将与视图控制器一起被释放,则无需将delegatedataSource 清零。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多