几个星期前我开始使用objective-c和iOS(值得记住),我提前为这个糟糕的图表道歉!!
上图显示了我对Web服务的调用结构。细箭头表示创建另一个对象的对象,而粗箭头表示对指向对象持有强(保留)引用的对象。
我认为这包含所谓的“循环引用”,并且在解除分配对象时会产生问题。
我明白,简单的答案就是取代一些强弱的引用,我很乐意做,除了我的项目也针对iOS 3.2(不是我的决定 - 我无法真正改变这个事实!)。所以,我认为我说我必须使用__unsafe_unretained,但我非常担心这些不会自动归零,因为当对象被解除分配时,我最终会遇到EXC_BAD_ACCESS问题。 ..
所以我的问题首先是我有循环引用。要解决,我将不得不使用__unsafe_unretained,这导致我的第二个问题:如何正确管理这些?
可能与之相关的问题是:NSURLConnection如何管理它的强引用?我从各种渠道获悉,它保留了代表团吗?所以...如果我保留一个NSURLConnection,(也是它的代表)并保留我,这也是一个循环引用,不是吗?它如何解决我的问题?
非常欢迎任何建议!
此致 尼克
答案 0 :(得分:6)
当父对象具有对子对象的引用时,它应该使用强引用。当一个孩子引用它的父对象时,它应该使用一个弱引用,也就是unsafe_unretained。
按照惯例,iOS中的委托关系通常是弱引用,因此您会发现Apple自己的类上的大多数委托属性都被声明为unsafe_unretained。
因此,您的控制器会保留其正在使用的服务,但这些服务只会微弱地链接回控制器。这样,如果控制器被释放,整个批次可以安全地处理掉,而不需要任何循环引用。
这样做的危险在于,如果Web服务正在执行一些长时间运行的任务,并且控制器在它完成之前被释放,那么该服务将留下一个指向它现在已释放的委托的悬空指针。如果它试图向代理发送消息,例如“我已经完成”,它将崩溃。
有一些方法可以帮助解决这个问题(它们不是相互排斥的 - 你应该尽可能地尝试这些方法):
1)始终在控制器的dealloc方法中将服务的委托属性设置为nil。这样可以确保在释放控制器时,对它的委托引用设置为nil(粗略的,手动等效于ARC的弱引用自动执行的操作)。
2)在创建自己的具有委托的服务类时,让它们在运行时保留它们的委托,然后在委托完成后释放委托。这样,当服务仍然发送消息时,委托对象不能被释放,但是一旦服务完成它仍然会被释放(NSTimer和NSURLConnections都以这种方式工作 - 它们在运行时保留它们的委托并释放它当它们完成时。)
3)尽量不要像视图控制器这样的瞬态事件拥有长期运行的服务。考虑创建拥有您的服务的单件对象(共享静态对象实例),这样服务可以在后台执行它的工作,无论视图层中发生了什么。控制器仍然可以调用该服务,但不拥有它 - 该服务由一个静态对象拥有,该静态对象将在应用程序运行期间存在,因此不存在泄漏或过早发布的风险。该服务可以通过NSNotifications而不是委托调用与控制器通信,因此不需要它可以引用可能消失的对象。 NSNotifications是在多个类之间进行通信而不创建循环引用的好方法。
答案 1 :(得分:1)
您的所有问题和疑虑都是正确的,以前使用assign
(现在更好地命名为__unsafe_unretained
)的问题是Apple为weak
开发自动归零的原因。但是我们已经和assign
代表合理安全地处理了多年,所以你怀疑,有办法做到这一点。
首先,作为一种惯例,当您释放您委派的对象时,您应该始终将自己清除为委托。预ARC,这传统上是在dealloc
:
- (void)dealloc {
[tableView_ setDelegate:nil];
[tableView_ release];
tableView_ = nil;
}
如果setDelegate:nil
为dealloc
,您仍应在delegate
中加入__unsafe_unretained
。这将解决最常见的问题形式(在委托对象之前取消分配委托时)。
关于NSURLConnection
,您也保留其委托也是正确的。这是可以的,因为它的生命周期通常比其委托短得多(相对于表视图委托,它几乎总是与表视图具有相同的生命周期)。请参阅“How to work around/handle delegation EXC_BAD_ACCESS errors? Obj C”,以便在ARC之前的背景下对此进行更多讨论(相同的概念适用于新的强弱世界)。