调用-retainCount被认为是有害的
或者,为什么我没有在我的暑假中使用retainCount
这篇文章的目的是要详细介绍一下这个臭名昭着的方法的原因和retainCount
,以便整合围绕SO的相关信息。
-
基础知识:不使用
retainCount
的官方原因是什么? 有没有什么情况可能有用? 应该做些什么呢?**随意编辑。 -
历史/解释:为什么苹果在
NSObject
协议中提供这种方法,如果它不打算被使用? 苹果的代码是否依赖于retainCount
用于某种目的? 如果是这样,为什么不把它藏在某处? -
为了更深入的理解:对象可能具有不同于用户代码假设的保留数的原因是什么? 你能举出框架代码可能使用的标准程序的例子吗? 是否有任何已知的情况下保留计数总是不同于新用户可能期望的?
-
还有什么你认为值得关于
retainCount
?
*对于Objective-C和Cocoa来说,新的编程人员经常会对参考计数scheme进行纠正,或者至less是误解。 教程的解释可能会提到保留计数,根据这些解释,当您调用retain
, alloc
, copy
等等时,向上调一,当您调用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,然后我就可以释放它,因为知道没有别的东西在保持,所以在垃圾收集环境中这显然不能正常工作,并且有更好的方法来做到这一点。 但是,这仍然是我见过的唯一“有效的”用例,并不是很多人会做的。