调用 -retainCount 被认为是有害的 [英] Calling -retainCount Considered Harmful

查看:43
本文介绍了调用 -retainCount 被认为是有害的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这篇文章旨在征集关于那个臭名昭著的方法 retainCount 的原因和原因的详细文章,以整合围绕 SO 的相关信息.*

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

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

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

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

<小时>

<子>*不熟悉 Objective-C 和 Cocoa 的程序员经常会遇到或至少误解引用计数方案.教程解释可能会提到保留计数,当你调用 retainalloccopy 等时,它(根据这些解释)增加一.,并在您调用 release 时减一(以及在将来调用 autorelease 时).

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

我希望这会变成一个常见问题解答,我们的任何一位愿意写一篇文章的专家提供的一页很好的信息性文章/讲座,新的 Cocoa-heads 可以在他们想知道时指出关于 retainCount.

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

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

解决方案

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

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

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

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

1) 由于多种原因,该值可能会有所不同.线程本身就足以让我们永远不要相信它.

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

<块引用>

在任何情况下它可能有用吗?

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

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

这在极少数情况下可能是有用的诊断方法.可用于检测:

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

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

  • 自动释放级别:自动释放、自动释放池和保留/自动释放周期都需要付出代价.如果您需要最大限度地减少或减少内存使用和/或增长,您可以使用此方法来检测过多的情况.

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

<块引用>

应该怎么做?

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

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

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

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

<块引用>

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

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

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

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

<块引用>

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

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

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

<块引用>

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

作为调试辅助工具毫无用处.学习使用泄漏和僵尸分析,并经常使用它们——即使你已经掌握了引用计数.

<小时>

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

Or, Why I Didn't Use retainCount On My Summer Vacation

This post is intended to solicit detailed write-ups about the whys and wherefores of that infamous method, retainCount, in order to consolidate the relevant information floating around SO.*

  1. The basics: What are the official reasons to not use retainCount? Is there ever any situation at all when it might be useful? What should be done instead?** Feel free to editorialize.

  2. Historical/explanatory: Why does Apple provide this method in the NSObject protocol if it's not intended to be used? Does Apple's code rely on retainCount for some purpose? If so, why isn't it hidden away somewhere?

  3. For deeper understanding: What are the reasons that an object may have a different retain count than would be assumed from user code? Can you give any examples*** of standard procedures that framework code might use which cause such a difference? Are there any known cases where the retain count is always different than what a new user might expect?

  4. Anything else you think is worth metioning about retainCount?


* Coders who are new to Objective-C and Cocoa often grapple with, or at least misunderstand, the reference-counting scheme. Tutorial explanations may mention retain counts, which (according to these explanations) go up by one when you call retain, alloc, copy, etc., and down by one when you call release (and at some point in the future when you call autorelease).

A budding Cocoa hacker, Kris, could thus quite easily get the idea that checking an object's retain count would be useful in resolving some memory issues, and, lo and behold, there's a method available on every object called retainCount! Kris calls retainCount on a couple of objects, and this one is too high, and that one's too low, and what the heck is going on?! So Kris makes a post on SO, "What's wrong with my memory management?" and then a swarm of <bold>, <large> letters descend saying "Don't do that! You can't rely on the results.", which is well and good, but our intrepid coder may want a deeper explanation.

I'm hoping that this will turn into an FAQ, a page of good informational essays/lectures from any of our experts who are inclined to write one, that new Cocoa-heads can be pointed to when they wonder about retainCount.

** I don't want to make this too broad, but specific tips from experience or the docs on verifying/debugging retain and release pairings may be appropriate here.

***In dummy code; obviously the general public don't have access to Apple's actual code.

解决方案

The basics: What are the official reasons to not use retainCount?

Autorelease management is the most obvious -- you have no way to be sure how many of the references represented by the retainCount are in a local or external (on a secondary thread, or in another thread's local pool) autorelease pool.

Also, some people have trouble with leaks, and at a higher level reference counting and how autorelease pools work at fundamental levels. They will write a program without (much) regard to proper reference counting, or without learning ref counting properly. This makes their program very difficult to debug, test, and improve -- it's also a very time consuming rectification.

The reason for discouraging its use (at the client level) is twofold:

1) The value may vary for so many reasons. Threading alone is reason enough to never trust it.

2) You still have to implement correct reference counting. retainCount will never save you from imbalanced reference counting.

Is there ever any situation at all when it might be useful?

You could in fact use it in a meaningful way if you wrote your own allocators or reference counting scheme, or if your object lived on one thread and you had access to any and all autorelease pools it could exist in. This also implies you would not share it with any external APIs. The easy way to simulate this is to create a program with one thread, zero autorelease pools, and do your reference counting the 'normal' way. It's unlikely that you'll ever need to solve this problem/write this program for anything other than "academic" reasons.

As a debugging aid: you could use it to verify that the retain count is not unusually high. If you take this approach, be mindful of the implementation variances (some are cited in this post), and don't rely on it. Don't even commit the tests to your SCM repository.

This may be a useful diagnostic in extremely rare circumstances. It can be used to detect:

  • Over-retaining: An allocation with a positive imbalance in retain count would not show up as a leak if the allocation is reachable by your program.

  • An object which is referenced by many other objects: One illustration of this problem is a (mutable) shared resource or collection which operates in a multithreaded context - frequent access or changes to this resource/collection can introduce a significant bottleneck in your program's execution.

  • Autorelease levels: Autoreleasing, autorelease pools, and retain/autorelease cycles all come with a cost. If you need to minimize or reduce memory use and/or growth, you could use this approach to detect excessive cases.

From commentary with Bavarious (below): a high value may also indicate an invalidated allocation (dealloc'd instance). This is completely an implementation detail, and again, not usable in production code. Messaging this allocation would result in a error when zombies are enabled.

What should be done instead?

If you're not responsible for returning the memory at self (that is, you did not write an allocator), leave it alone - it is useless.

You have to learn proper reference counting.

For a better understanding of release and autorelease usage, set up some breakpoints and understand how they are used, in what cases, etc. You'll still have to learn to use reference counting correctly, but this can aid your understanding of why it's useless.

Even simpler: use Instruments to track allocs and ref counts, then analyze the ref counting and callstacks of several objects in an active program.

Historical/explanatory: Why does Apple provide this method in the NSObject protocol if it's not intended to be used? Does Apple's code rely on retainCount for some purpose? If so, why isn't it hidden away somewhere?

We can assume that it is public for two primary reasons:

1) Reference counting proper in managed environments. It's fine for the allocators to use retainCount -- really. It's a very simple concept. When -[NSObject release] is called, the ref counter (unless overridden) may be called, and the object can be deallocated if retainCount is 0 (after calling dealloc). This is all fine at the allocator level. Allocators and zones are (largely) abstracted so... this makes the result meaningless for ordinary clients. See commentary with bbum (below) for details on why retainCount cannot be equal to 0 at the client level, object deallocation, deallocation sequences, and more.

2) To make it available to subclassers who want a custom behavior, and because the other reference counting methods are public. It may be handy in a few cases, but it's typically used for the wrong reasons (e.g. immortal singletons). If you need your own reference counting scheme, then this family may be worth overriding.

For deeper understanding: What are the reasons that an object may have a different retain count than would be assumed from user code? Can you give any examples*** of standard procedures that framework code might use which cause such a difference? Are there any known cases where the retain count is always different than what a new user might expect?

Again, a custom reference counting schemes and immortal objects. NSCFString literals fall into the latter category:

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

Anything else you think is worth mentioning about retainCount?

It's useless as a debugging aid. Learn to use leak and zombie analyses, and use them often -- even after you have a handle on reference counting.


Update: bbum has recently posted an article entitled retainCount is useless. The article contains a thorough discussion of why -retainCount isn’t useful in the vast majority of cases.

这篇关于调用 -retainCount 被认为是有害的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆