【问题标题】:Why are Objective-C delegates usually given the property assign instead of retain?为什么 Objective-C 委托通常被赋予属性分配而不是保留?
【发布时间】:2010-10-29 10:56:08
【问题描述】:

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

直接来自 Scott 的博客:

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

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

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

谢谢!

【问题讨论】:

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

标签: objective-c ios cocoa delegates


【解决方案1】:

您避免保留代表的原因是您需要避免保留循环:

A 创造 B A 将自己设置为 B 的代表 … A 被其所有者释放

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

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

【讨论】:

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

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

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

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

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

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


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

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

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

【讨论】:

  • NSURLConnection 保留其代表。
  • 是的,使用weak,但这并不能回答最初的问题:为什么Apple 使用assign 而不是weak
  • @wcochran:最初的问题是“为什么给定委托属性 assign 而不是 retain ”; weak 在被询问时不存在。您的问题是另一个问题,您应该单独提出。我很乐意回答。
  • @wcochran 和 Peter,这个问题有没有在其他地方问过?
【解决方案3】:

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

【讨论】:

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

其中一个原因是避免保留循环。 只是为了避免 A 和 B 两个对象相互引用并且没有一个从内存中释放的情况。

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

【讨论】:

  • 那是分别从 OP 的报价和接受的答案中复制而来的,不是吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-23
相关资源
最近更新 更多