ChatGPT解决这个技术问题 Extra ChatGPT

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

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

直接来自 Scott 的博客:

“assign 关键字将生成一个 setter,它直接将值分配给实例变量,而不是复制或保留它。这最适用于 NSInteger 和 CGFloat 等原始类型,或者您不直接拥有的对象,例如委托。”

您不直接拥有委托对象是什么意思?我通常会保留我的代表,因为如果我不希望他们陷入深渊,保留会为我解决这个问题。我通常将 UITableViewController 从其各自的数据源和委托中抽象出来。我也保留了那个特定的对象。我想确保它永远不会消失,所以我的 UITableView 总是有它的委托。

有人可以进一步解释我在哪里/为什么错了,这样我就可以理解 Objective-C 2.0 编程中使用委托而不是保留的分配属性的常见范例?

谢谢!

重新标记为“代表”而没有“iphone”。
为什么委托分配而不是复制(如NSString?)

A
Andrew Pouliot

避免保留代表的原因是您需要避免保留周期:

A 创建 BA 将自己设置为 B 的代表…… A 由其所有者释放

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

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


我不同意,迈克。我刚刚发现了一个问题,即模态有一个解除模态的委托。但是当我在模态中发出内存警告时,它会释放委托。然后当我去关闭我的模式时,代表为零。碰撞。
好的,所以我不同意,但你说得对,这是一个设计缺陷。我发现我正在通过真正解雇者的子类传递解雇电话。该子类已被释放,并且无法传递给模态的容器委托以关闭。我将其更改为将指针传递给最终代表,并且该指针在内存警告时没有被释放,一切都很好。
您的代码永远不应以 nil 委托导致其崩溃的方式编写。只有拥有对象应该有一个拥有引用。当 dealloc'd 时,它必须将拥有对象的委托设置为 nil,然后才能释放它们。然后任何发送给 nil 代表的消息都会被忽略。但是,在消息中传递 nil 对象可能会崩溃。只要确保你不以这种方式与代表打交道。
等等——这不是 weak 所做的吗?问题是为什么使用 assign 而不是 weak
@wcochran:不,这个问题是为什么使用 assign 而不是 retain。这个问题比 ARC 更老; weakstrong(后者是 retain 的同义词)直到 ARC 被引入才存在。您应该分别询问有关 weakassign 的问题。
P
Peter Hosey

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

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

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

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

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

附录(2012 年 5 月 19 日添加):在 ARC 下,您应该使用 weak 而不是 assign。当对象死亡时,弱引用会自动设置为 nil,从而消除委托对象最终将消息发送给死亡委托的可能性。

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

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


NSURLConnection 保留其委托。
是的,使用 weak,但这并不能回答最初的问题:为什么 Apple 使用 assign 而不是 weak
@wcochran:最初的问题是“为什么给定委托属性 assign 而不是 retain ”;询问时 weak 不存在。您的问题是另一个问题,您应该单独提出。我很乐意回答。
@wcochran 和彼得,这个问题在其他地方被问过吗?
K
Kendall Helmstetter Gelner

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


“请注意,当您有一个正在分配的委托时,无论何时要解除分配对象,始终将该委托值设置为 nil 非常重要” 为什么?
因为任何未设置的引用在对象被释放后都将无效(指向不再分配给预期对象类型的内存) - 如果您尝试使用它,则会导致崩溃。调试器中的一个迹象是,当调试器声称某个变量的类型与变量实际声明的类型完全错误时。
仅当您作为委托的对象被另一个源(例如计时器或其他异步回调)保留时,才需要这样做。否则,它会在您释放后被释放,并且不会尝试调用委托方法。
@Andrew:是的,但是如果您总是习惯于消除代表,那么您将不会忘记重要的时间,或者如果您不小心过度保留了一个持有的对象并且它仍然存在。如果您取消委托,则结果只是泄漏,而不是泄漏后崩溃。
P
Puneet Sharma

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

Acutally assign 最适合原始类型,如 NSInteger 和 CGFloat,或您不直接拥有的对象,如委托。


那是分别从OP的报价和接受的答案中复制而来的,不是吗?