呼叫-retainCount被认为是有害的

Jos*_*ell 38 cocoa reference-counting objective-c retaincount

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

这篇文章的目的是征求关于臭名昭着的方法的原因和原因的详细报道retainCount,以巩固围绕SO的相关信息.*

  1. 基础知识:不使用的官方理由是什么retainCount?是否有过任何情况下当它可能是有用的?应该做些什么?**随意编辑.

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

  3. 为了更深入地理解:对象可能具有与用户代码假设不同的保留计数的原因是什么?您能否举例说明框架代码可能会使用哪些标准程序导致这种差异?是否有任何已知的情况,保留计数总是与新用户可能期望的不同?

  4. 你认为还有什么值得关注的retainCount吗?


*不熟悉Objective-C和Cocoa的编码人员经常努力解决或至少误解参考计数方案.教程解释可能提及保留计数,这(根据这些解释)当你打电话上去一个retain,alloc,copy由一个当你调用等,向下release(并在当你调用未来的某一时刻autorelease).

因此,一个崭露头角的可可黑客克里斯可以很容易地认识到,检查一个对象的保留计数对于解决一些内存问题是有用的,而且,有一种方法可用于每个被调用的对象retainCount!克里斯打电话retainCount给几个对象,这个对象太高了,而且这个对象太低了,到底发生了什么?!因此,Kris在SO上发帖,"我的记忆管理出了什么问题?" 然后一群<bold>,<large>字母下降说"不要那样做!你不能依靠结果.",这是好的,但我们的强悍编码器可能需要更深入的解释.

我希望这会变成一个常见问题解答,一个很好的信息论文/讲座的页面,我们的任何专家都倾向于写一个,当他们想知道时可以指出新的可可头retainCount.

**我不想让它过于宽泛,但是经验或有关验证/调试保留和释放配对的文档的具体提示可能适合此处.

***在虚拟代码中; 很明显,一般公众无法访问Apple的实际代码.

jus*_*tin 29

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

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

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

阻止其使用(在客户端级别)的原因有两个:

1)该值可能因多种原因而有所不同.仅仅是线程就足以让人永不信任它.

2)您仍然必须实现正确的引用计数.retainCount永远不会让你免于不平衡的引用计数.

什么时候有可能有用吗?

可以在实际上使用它以一种有意义的方式,如果你写你自己的分配器或引用计数方案,或者如果你的对象住在一个线程,你有机会访问的任何所有的自动释放池,它能够在存在的.这也意味着你不会与任何外部API共享.模拟这个的简单方法是创建一个包含一个线程,零自动释放池的程序,并按照"正常"方式进行引用计数.您不太可能需要解决此问题/将此程序编写为"学术"原因之外的任何其他内容.

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

在极少数情况下,这可能是一种有用的诊断方法.它可以用来检测:

  • 过度保留:如果您的程序可以达到分配,则保留计数中存在正不平衡的分配不会显示为泄漏.

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

  • 自动释放水平:自动释放,自动释放池和保留/自动释放循环都需要付出代价.如果您需要最小化或减少内存使用和/或增长,您可以使用此方法来检测过多的情况.

从Bavarious的评论(下面):高值也可能表示无效的分配(dealloc'd实例).这完全是一个实现细节,同样不能在生产代码中使用.在启用僵尸时,传递此分配将导致错误.

应该做什么呢?

如果您不负责返回内存self(也就是说,您没有编写分配器),请不要管它 - 这是没用的.

你必须学习正确的引用计数.

为了更好地理解发布和自动释放的使用,设置一些断点并了解它们的使用方式,在什么情况下等等.您仍然需要学会正确使用引用计数,但这可以帮助您理解为什么它没用.

更简单:使用Instruments跟踪alloc和ref计数,然后分析活动程序中几个对象的引用计数和callstack.

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

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

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

2)使其可用于需要自定义行为的子类,以及其他引用计数方法是公共的.它在某些情况下可能很方便,但它通常用于错误的原因(例如不朽的单身人士).如果您需要自己的参考计数方案,那么这个家庭可能值得重写.

为了更深入地理解:对象可能具有与用户代码假设不同的保留计数的原因是什么?您能否举例说明框架代码可能会使用哪些标准程序导致这种差异?是否有任何已知的情况,保留计数总是与新用户可能期望的不同?

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

NSLog(@"%qu", [@"MyString" retainCount]); 
// Logs: 1152921504606846975
Run Code Online (Sandbox Code Playgroud)

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

它作为调试辅助工具毫无用处.了解如何使用泄漏和僵尸分析,并经常使用它们-即使以后你有引用计数的句柄.


更新: bbum最近发布了一篇名为retainCount的文章是没用的.本文详细讨论了为什么-retainCount在绝大多数情况下没有用处.

  • 根据定义,当保留计数转换为零时,将取消分配支持该对象的分配.对该对象的任何后续消息(包括-retainCount)在行为中都是未定义的.要解决此问题,您必须使后释放消息有效.因此,您永远不能为新对象重复使用任何地址,因为这违反了合同(除非您还添加了一些要求,即每个分配都有一个关联的UUID). (4认同)
  • 谢谢.这尤其是一个很好的洞察力:"仅仅是线程就足以让人不相信它." (3认同)
  • 不返回零只是一个实现细节,因为返回零将需要使得对已释放的对象进行消息是合理的(这可能也需要永远不会在同一地址分配另一个对象). (3认同)
  • 你当然可以沿着这条路走下去,但这样做会让你完全摆脱Cocoa/iOS运行时的现实.然而,它也得到了全面的支持.如果需要,您可以定义自己的根类,并且可以创建所需的任何分配模式.只是不要将您的对象传递给框架API!(这实际上是一个非常有趣的心理练习 - 多年来我创建了一些根类来探索不同的模型.) (3认同)
  • 一个注意事项:`-retainCount`永远不会变为零. (2认同)