调用-retainCount被认为是有害的

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

这篇文章的目的是要详细介绍一下这个臭名昭着的方法的原因和retainCount ,以便整合围绕SO的相关信息。

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

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

  3. 为了更深入的理解:对象可能具有不同于用户代码假设的保留数的原因是什么? 你能举出框架代码可能使用的标准程序的例子吗? 是否有任何已知的情况下保留计数总是不同于新用户可能期望的?

  4. 还有什么你认为值得关于retainCount


*对于Objective-C和Cocoa来说,新的编程人员经常会对参考计数scheme进行纠正,或者至less是误解。 教程的解释可能会提到保留计数,根据这些解释,当您调用retainalloccopy等等时,向上调一,当您调用release (以及在将来的某个时刻调用autorelease )。

一个正在萌芽的Cocoa黑客Kris可以很容易地认识到,检查一个对象的保留数对于解决一些内存问题是有用的,而且你会发现在每个对象上有一个叫做retainCount 克里斯在一些物体上调用retainCount ,这个值太高了,太低了,到底是怎么回事?! 所以Kris在SO上做了一个post,“我的memory management出了什么问题? 然后一群<大写>,<大写字母下降,说:“不要那样做,不能靠结果”,这是好的,但是我们无畏的编码者可能需要更深的解释。

我希望这将成为一个常见问题解答,我们的任何专家都倾向于写一个好的信息散文/讲座页面,当新的cocoa头在怀疑retainCount时可以指出。

**我不想把这个过于宽泛,但是具体的经验或者关于validation/debugging的文档保留和释放配对在这里可能是合适的。

***在虚拟代码中; 显然普通大众不能访问苹果的实际代码。

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

自动释放pipe理是最明显的 – 你无法确定由retainCount表示的引用有retainCount在本地或外部(在一个辅助线程或另一个线程的本地池)自动释放池中。

此外,有些人在泄漏问题上有困难,在更高的层面上引用计数以及自动释放池如何在基本水平上工作。 他们会写一个程序,没有(很多)正确的引用计数,或者没有学习正确的计数。 这使得他们的程序非常难以debugging,testing和改进 – 这也是一个非常耗时的纠正。

不鼓励使用(在客户端)的原因是双重的:

1)价值可能会有很多原因。 只有线程是足够的理由,永远不要相信它。

2)您仍然需要执行正确的参考计数。 retainCount永远不会将您从不平衡的引用计数中拯救出来。

有没有什么情况可能有用?

如果你编写自己的分配器或引用计数scheme,或者你的对象存在于一个线程中,并且你可以访问它可能存在的任何所有的autorelease池,你实际上可以以一种有意义的方式使用它。这也意味着你不会与任何外部API共享。 简单的方法来模拟这个是创build一个线程,零自动释放池的程序,并做你的引用计数的“正常”的方式。 除了“学术”原因之外,你不可能解决这个问题/写这个程序。

作为一个debugging帮助:您可以使用它来validation保留计数不是特别高。 如果你采取这种方法,请注意实现差异(有些在本文中被引用),而不是依赖它。 甚至不要将testing提交给你的SCM库。

这在极其罕见的情况下可能是有用的诊断。 它可以用来检测:

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

  • 一个被许多其他对象引用的对象:这个问题的一个例子是在multithreading上下文中操作的(可变的)共享资源或集合 – 经常访问或更改此资源/集合可能会在程序执行过程中引入严重的瓶颈。

  • 自动释放级别:自动释放,自动释放池和保留/自动释放循环都会带来成本。 如果您需要最小化或减less内存使用和/或增长,则可以使用此方法来检测过多的情况。

从Bavarious(下面)的评论:高价值也可能表明一个无效的分配(dealloc'd实例)。 这完全是一个实现细节,而且在生产代码中不可用。 发送此分配将导致僵尸启用时出现错误。

应该做些什么呢?

如果你没有责任返回自己的内存(也就是说,你没有写分配器),不要pipe它 – 这是没有用的。

你必须学习适当的参考计数。

为了更好地理解发布和autorelease的用法,设置一些断点并理解它们是如何使用的,以及在什么情况下等等。你仍然必须学会正确地使用引用计数,但是这可以帮助你理解为什么它是无用的。

甚至更简单:使用工具来跟踪分配和参考计数,然后分析活动程序中几个对象的引用计数和调用堆栈。

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

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

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

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

为了更深入的理解:对象可能具有不同于用户代码假设的保留数的原因是什么? 你能举出框架代码可能使用的标准程序的例子吗? 是否有任何已知的情况下保留计数总是不同于新用户可能期望的?

再次,自定义引用计数scheme和不朽的对象。 NSCFString文字属于后一类:

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

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

作为debugging帮助是没用的。 学习使用泄漏和僵尸分析,并经常使用它们 – 即使处理引用计数后也是如此。


更新: bbum最近发布了一篇名为“ retainCount is nothingless”的文章。 文章包含了深入的讨论,为什么在绝大多数情况下-retainCount没有用。

一般的经验法则是如果你使用这种方法,你最好确定你知道你在做什么。 如果你正在使用它来debugging内存泄漏,那么你做错了,如果你正在做这件事来看看对象是怎么回事,那么你做错了。

有一个我曾经使用过的案例,发现它很有用。 这是在做一个共享对象的caching,我想刷新对象时没有一个引用了。 在这种情况下,我一直等到retainCount等于1,然后我就可以释放它,因为知道没有别的东西在保持,所以在垃圾收集环境中这显然不能正常工作,并且有更好的方法来做到这一点。 但是,这仍然是我见过的唯一“有效的”用例,并不是很多人会做的。

Interesting Posts