38

或者,为什么我没有retainCount在暑假使用

这篇文章旨在征求关于这种臭名昭著的方法的原因和原因的详细文章,retainCount以整合围绕 SO 浮动的相关信息。*

  1. 基础知识:不使用的官方原因是什么retainCount?是否有任何可能有用的情况?应该怎么做?** 随意编辑。

  2. 历史/解释:如果不打算使用,为什么 Apple 会在NSObject协议中提供此方法?retainCountApple的代码是否出于某种目的而依赖?如果是这样,为什么不把它藏在某个地方?

  3. 为了更深入地理解:对象的保留计数可能与用户代码中假设的不同的原因是什么?您能否给出框架代码可能使用的标准过程的任何示例***,这些示例会导致这种差异?是否存在任何已知情况,其中保留计数总是与新用户的预期不同?

  4. 你认为还有什么值得一提的retainCount吗?


* 不熟悉 Objective-C 和 Cocoa 的编码人员经常会遇到或至少误解引用计数方案。教程解释可能会提到保留计数,它(根据这些解释)在您调用retainalloccopy等时增加一,在您调用时减少一release(以及在将来的某个时间调用autorelease)。

因此,一个崭露头角的 Cocoa 黑客 Kris 可以很容易地得出这样的想法:检查对象的保留计数将有助于解决一些内存问题,而且,你瞧,每个对象都有一个可用的方法,称为retainCount! Kris调用retainCount了几个对象,这个太高了,那个太低了,这到底是怎么回事?!所以 Kris 在 SO 上发了一个帖子,“我的内存管理出了什么问题?” 然后一大群 <bold>、<large> 字母下降说“不要那样做!你不能依赖结果。”这很好,但我们勇敢的编码员可能需要更深入的解释。

我希望这会变成一个常见问题解答,这是一页来自我们任何一位倾向于撰写一篇文章的专家的优秀信息论文/讲座,当新的 Cocoa-heads 对retainCount.

** 我不想把它说得太宽泛,但是来自经验的具体提示或关于验证/调试保留和释放配对的文档可能在这里是合适的。

***在虚拟代码中;显然,公众无法访问 Apple 的实际代码。

4

2 回答 2

29

基础知识:不使用retainCount 的官方理由是什么?

自动释放管理是最明显的——您无法确定有多少由 代表的引用retainCount在本地或外部(在辅助线程上,或在另一个线程的本地池中)自动释放池中。

此外,有些人在泄漏、更高级别的引用计数以及自动释放池如何在基本级别上工作时遇到问题。他们将编写一个程序,而不(太多)考虑正确的引用计数,或者没有正确学习引用计数。这使得他们的程序很难调试、测试和改进——这也是一个非常耗时的修正。

不鼓励使用它(在客户端级别)的原因有两个:

1) 由于很多原因,该值可能会有所不同。单单线程就是永远不要相信它的理由。

2)您仍然必须实施正确的引用计数。retainCount永远不会使您免于不平衡的引用计数。

是否有任何可能有用的情况?

如果您编写自己的分配器或引用计数方案,或者如果您的对象位于一个线程上并且您可以访问它可能存在的任何和所有自动释放池,那么您实际上可以以有意义的方式使用它。这也意味着您不会与任何外部 API 共享它。模拟这一点的简单方法是创建一个具有一个线程、零个自动释放池的程序,并以“正常”方式进行引用计数。除了“学术”原因之外,您不太可能需要解决这个问题/编写这个程序。

作为调试帮助:您可以使用它来验证保留计数是否异常高。如果您采用这种方法,请注意实现差异(本文中引用了一些),并且不要依赖它。甚至不要将测试提交到您的 SCM 存储库。

在极为罕见的情况下,这可能是一种有用的诊断方法。它可用于检测:

  • 过度保留:如果您的程序可以访问分配,则保留计数正不平衡的分配不会显示为泄漏。

  • 一个被许多其他对象引用的对象:这个问题的一个例子是在多线程上下文中运行的(可变)共享资源或集合 - 频繁访问或更改此资源/集合可能会在程序执行中引入重大瓶颈。

  • 自动释放级别:自动释放、自动释放池和保留/自动释放周期都需要成本。如果您需要最小化或减少内存使用和/或增长,您可以使用这种方法来检测过多的案例。

来自 Bavarious 的评论(下):高值也可能表示分配无效(dealloc'd 实例)。这完全是一个实现细节,同样不能在生产代码中使用。启用僵尸时,向此分配发送消息会导致错误。

应该怎么做?

如果你不负责返回内存self(也就是说,你没有写分配器),别管它——它没用。

您必须学习正确的引用计数。

为了更好地理解释放和自动释放的使用,设置一些断点并了解它们是如何使用的,在什么情况下等等。你仍然需要学习正确使用引用计数,但这可以帮助你理解为什么它是无用的.

更简单:使用 Instruments 跟踪分配和引用计数,然后分析活动程序中多个对象的引用计数和调用堆栈。

历史/解释:如果不打算使用,为什么 Apple 会在 NSObject 协议中提供此方法?Apple 的代码是否出于某种目的依赖于 retainCount?如果是这样,为什么不把它藏在某个地方?

我们可以假设它是公开的,主要有两个原因:

1)在托管环境中正确的引用计数。分配器可以使用retainCount——真的。这是一个非常简单的概念。当被调用时,可能会调用 ref 计数器(除非被覆盖),如果为 0(调用 dealloc 之后)-[NSObject release],则可以释放对象。retainCount在分配器级别这一切都很好。分配器和区域(很大程度上)是抽象的,所以......这使得结果对普通客户毫无意义。retainCount有关为什么在客户端级别不能等于 0、对象解除分配、解除分配序列等的详细信息,请参阅 bbum 的评论(如下) 。

2) 使其对想要自定义行为的子类可用,并且因为其他引用计数方法是公共的。在少数情况下它可能很方便,但它通常用于错误的原因(例如不朽的单例)。如果您需要自己的引用计数方案,那么这个系列可能值得覆盖。

为了更深入地理解:对象的保留计数可能与用户代码中假设的不同的原因是什么?您能否给出框架代码可能使用的标准过程的任何示例***,这些示例会导致这种差异?是否存在任何已知情况,其中保留计数总是与新用户的预期不同?

同样,自定义引用计数方案和不朽对象。NSCFString文字属于后一类:

NSLog(@"%qu", [@"MyString" retainCount]); 
// Logs: 1152921504606846975

关于retainCount,您认为还有什么值得一提的吗?

它作为调试辅助是没有用的。学习使用泄漏和僵尸分析,并经常使用它们——即使你掌握了引用计数之后。


更新: bbum 最近发了一篇文章,题为retainCount is useless。这篇文章详细讨论了为什么-retainCount在绝大多数情况下没有用。

于 2011-04-26T05:19:43.443 回答
1

一般的经验法则是,如果你使用这种方法,你最好确定你知道你在做什么。如果您使用它来调试内存泄漏,那么您做错了,如果您这样做是为了查看对象发生了什么,那么您做错了。

有一个案例,我用过它,发现它很有用。那是在做一个共享对象缓存时,我想在没有任何东西引用它时刷新对象。在这种情况下,我一直等到 retainCount 等于 1,然后我可以释放它,因为我知道没有其他东西在保留它,这显然在垃圾收集环境中无法正常工作,并且有更好的方法来做到这一点。但这仍然是我见过的唯一“有效”用例,而且不是很多人会做的事情。

于 2011-04-25T22:45:53.063 回答