为什么通常给Objective-C委托赋予属性而不是保留?


176

我正在浏览由史蒂文森(Scott Stevenson)维护的精彩博客,并且我试图理解一个基本的Objective-C概念,即向代表分配“分配”属性与“保留”属性。请注意,在垃圾回收环境中两者都是相同的。我最关心的是基于非GC的环境(例如:iPhone)。

直接来自Scott的博客:

“ assign关键字将生成一个设置器,该设置器将值直接分配给实例变量,而不是复制或保留该值。这最适合诸如NSInteger和CGFloat之类的原始类型,或不直接拥有的对象(例如委托)。”

您不直接拥有委​​托对象是什么意思?我通常会保留我的代表,因为如果我不希望他们走入深渊,保留会为我解决。我通常将UITableViewController从其各自的dataSource和委托中分离出来。我也保留该特定对象。我想确保它永远不会消失,所以我的UITableView总是有它的委托。

有人可以进一步解释我错在哪里/为什么错了,以便我能了解在Objective-C 2.0编程中使用委托上的assign属性而不是retain的常见范例吗?

谢谢!


重新标记为“代表”而没有“ iphone”。
奎因·泰勒,

为什么委托分配而不是复制(例如NSString?)
OMGPOP,2014年

Answers:


175

避免保留委托的原因是,您需要避免保留周期:

A创建B A将自己设置为B的委托人…A由其所有者释放

如果B保留了A,则A将不会被释放,因为B拥有A,因此A的dealloc将永远不会被调用,从而导致A和B都泄漏。

您不必担心A消失,因为A拥有B并因此在dealloc中摆脱了它。


我不同意,迈克。我刚刚发现了一个问题,其中有一个模态有一个代表来解雇模态。但是,当我在模式中执行内存警告时,它将释放委托。然后,当我去解散我的模态时,代表为零。崩溃
保罗·夏皮罗

好的,所以我不同意,但是您是对的,这是设计缺陷。我发现我正在通过真正的解雇人的孩子班级转驳我的解雇电话。该子类已释放,并且无法传递给模式的容器委托以将其关闭。我将其更改为传递指向最终委托的指针,并且没有在内存警告时释放该指针,并且一切都很好。
保罗·夏皮罗

2
您的代码绝不能以nil委托导致崩溃的方式编写。仅拥有对象应具有拥有引用。取消分配时,必须在释放它们之前将拥有对象的委托设置为nil。然后,将忽略任何发送给nil委托的消息。但是,消息中传递nil对象可能会导致崩溃。只要确保您不以这种方式与代表打交道即可。
David Gish

等待-那不是什么weak吗?问题是为什么使用assign代替weak
wcochran

3
@wcochran:不,这个问题是为什么要使用assign而不是retain。这个问题早于ARC。weakstrong(与后者的代名词retain是不存在的),直到ARC进行了介绍。您应该分别问关于weakvs 的问题assign
Peter Hosey

44

因为发送委托消息的对象不拥有委托。

很多时候,这是相反的,例如当控制器将自己设置为视图或窗口的委托时:控制器拥有视图/窗口,因此,如果视图/窗口拥有其委托,则两个对象将彼此拥有。当然,这是一个保留周期,类似于具有相同结果的泄漏(应该死亡的对象仍然活着)。

在其他时候,这些对象是对等的:一个都不拥有另一个,可能是因为它们都属于同一个第三个对象。

无论哪种方式,带有委托的对象都不应保留其委托。

(顺便说一下,至少有一个例外。我不记得那是什么,我也不认为有充分的理由。)


附录(于2012-05-19添加):在ARC下,您应使用weak而不是assignnil当对象死亡时,弱引用将自动设置为,从而消除了委派对象最终将消息发送给无效委托的可能性。

如果出于某种原因而远离ARC,请至少将assign指向对象的属性更改为unsafe_unretained,这明确表明这是对对象的非保留但不归零的引用。

assign 仍然适用于ARC和MRC下的非对象值。


13
NSURLConnection保留其代表。

是的,请使用weak,但这不能回答最初的问题:苹果为什么要使用assign而不是weak
wcochran

@wcochran:最初的问题是“为什么要赋予委托属性assign而不是保留 ”?weak被问到时不存在。您的问题是一个不同的问题,您应该单独提出。我很乐意回答。
彼得·霍西

@wcochran和Peter,是否有人在其他地方提出了这个问题?
罗杰斯先生

17

请注意,当您有一个分配的委托时,无论何时要取消分配该对象,始终将委托值设置为nil非常重要-因此,如果对象没有分配,则应始终小心在dealloc中取消委托引用在其他地方也这样做。


“请注意,当您有一个分配的委托时,每次将要释放该对象时始终将委托值设置为nil非常重要”。为什么?
彼得·霍西

2
因为设置了任何引用,在释放对象之后(指向不再分配给预期对象的内存),该引用将无效-从而在尝试使用它时会导致崩溃。在调试器中的一个迹象是,当调试器声称某个变量的类型与实际声明的变量似乎完全错误时。
肯德尔·赫尔姆斯特·盖尔纳

1
仅当您作为其委托的对象被另一个源(例如计时器或其他异步回调)保留时,才需要这样做。否则,将在释放它后将其释放,并且不会尝试调用委托方法。
安德鲁·波利奥特

@Andrew:是的,但是如果您始终以淘汰代表作为惯例,那么您就不会忘记什么时候重要,或者如果您意外保留了一个保留的对象并且无论如何都会保留它。如果您取消委托,那么结果只是一个泄漏,而不是随后的崩溃。
肯德尔·赫尔姆斯特·盖尔纳

1

其背后的原因之一是避免保留周期。只是为了避免A和B这两个对象互相引用并且它们都不从内存释放的情况。

手动分配最适合NSInteger和CGFloat之类的原始类型,或不直接拥有的对象(例如委托)。


分别是从OP的报价和接受的答案中复制的,不是吗?
dakab
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.