3

几周前我开始使用objective-c和iOS(值得牢记),我提前为糟糕的图表道歉!

在此处输入图像描述

上图显示了我对 Web 服务的调用的结构。细箭头表示对象创建另一个对象,而粗箭头表示对象持有对指向对象的强(保留)引用。

我相信这包含所谓的“循环引用”,并且在释放对象时会产生问题。

我知道简单的答案是替换一些对弱引用的强引用,我很乐意这样做,除了我的项目也针对 iOS 3.2(不是我的决定——我真的无法改变这个事实!) . 所以,我认为我说我必须使用 __unsafe_unretained 是对的,但我很担心这些不会自动归零,因为当对象被释放时我会遇到 EXC_BAD_ACCESS 问题。 ..

所以我的问题首先是我有循环引用。要解决,我将不得不使用 __unsafe_unretained,这导致了我的第二个问题:如何正确管理这些?

一个可能相关的问题是: NSURLConnection 如何管理它的强引用?我从各种渠道听说它保留了它的代表?所以...如果我保留一个 NSURLConnection(并且也是它的代表)并且它保留了我,这也将是一个循环引用,不是吗?它如何解决我的问题?

非常欢迎任何建议!

问候,尼克

4

2 回答 2

6

当父对象有对子对象的引用时,它应该使用强引用。当子对象有对其父对象的引用时,它应该使用弱引用,即 unsafe_unretained。

按照惯例,iOS 中的委托关系通常是弱引用,因此您会发现 Apple 自己的类中的大多数委托属性都声明为 unsafe_unretained。

因此,您的控制器保留了它正在使用的服务,但这些服务仅弱链接回控制器。这样,如果控制器被释放,整个批次就可以安全地处理掉,而无需任何循环引用。

这样做的危险在于,如果 Web 服务正在执行一些长时间运行的任务,并且控制器在它完成之前被释放,则该服务会留下一个悬空指针,指向它现在已解除分配的委托。如果它试图向代理发送消息,例如“我已经完成”,它将崩溃。

有一些方法可以帮助解决这个问题(它们不是相互排斥的——你应该尽可能地尝试全部):

1) 始终在控制器的 dealloc 方法中将服务的委托属性设置为 nil。这确保了当控制器被释放时,对它的委托引用被设置为 nil(有点粗糙,手动等效于 ARC 的弱引用自动执行的操作)。

2) 创建您自己的具有委托的服务类时,让它们在运行时保留其委托,然后在完成后释放委托。这样,当服务仍在向它发送消息时,委托对象不能被释放,但是一旦服务完成,它仍然会被释放(NSTimer 和 NSURLConnections 都以这种方式工作 - 它们在运行时保留它们的委托并释放它当他们完成时)。

3) 尽量不要让像视图控制器这样的临时服务拥有长期运行的服务。考虑创建拥有您的服务的单例对象(共享静态对象实例),这样服务就可以在后台完成它的工作,而不管视图层中发生了什么。控制器仍然可以调用服务,但不拥有它 - 服务由一个静态对象拥有,该对象将在应用程序运行期间存在,因此不存在泄漏或过早发布的风险。该服务可以通过 NSNotifications 而不是委托调用与控制器通信,因此它不需要引用可能会消失的对象。NSNotifications 是在多个类之间进行通信而无需创建循环引用的好方法。

于 2012-01-30T14:08:44.920 回答
1

您的所有问题和疑虑都是正确的,而之前使用assign(现在更好地命名为__unsafe_unretained)的这个问题就是 Apple 为weak. assign但多年来,我们一直在合理安全地处理代表,所以正如您所怀疑的,有办法做到这一点。

首先,作为一个实践问题,当您释放一个您被委托的对象时,您应该始终将自己清除为委托。Pre-ARC,这通常是在dealloc

- (void)dealloc {
  [tableView_ setDelegate:nil];
  [tableView_ release];
  tableView_ = nil;
}

您仍然应该将其包含setDelegate:nil在您的deallocif delegateis 中__unsafe_unretained。这将解决问题的最常见形式(当委托在委托对象之前被释放时)。

关于NSURLConnection,您保留其代表也是正确的。这没关系,因为它的生命周期通常比它的委托短得多(相对于几乎总是与表格视图具有相同生命周期的表格视图委托)。请参阅“如何解决/处理委派 EXC_BAD_ACCESS 错误?Obj C ”,以了解在 ARC 之前的上下文中对此的更多讨论(相同的概念适用于强/弱的新世界)。

于 2012-01-30T14:23:34.773 回答