何时使用-retainCount?

113
我想了解您迄今为止使用 -retainCount 的情况,以及在使用它时可能发生的问题。
谢谢。

5
永远不要使用 -retainCount - holex
3
除非有必要,否则不应该这样做。偶尔这样做可以帮助理解正在发生的事情,但是需要明确,有时候这可能会严重误导。当然,在使用ARC时,这完全不允许。 - Hot Licks
11个回答

248
你不应该使用-retainCount,因为它永远不会告诉你任何有用的信息。Foundation和AppKit/UIKit框架的实现是不透明的,你不知道什么被保留、为什么被保留、谁在保留它、何时被保留等等。
例如:
- 你可能认为[NSNumber numberWithInt:1]retainCount值应为1,但实际上却是2。 - 你可能认为@"Foo"retainCount值应为1,但实际上却是1152921504606846975。 - 你可能认为[NSString stringWithString:@"Foo"]retainCount值应为1,但实际上却同样是1152921504606846975。
基本上,由于任何东西都可以保留一个对象(从而改变其retainCount值),而且由于大多数运行应用程序的代码并不是你编写的,所以对象的retainCount值是毫无意义的。
如果你试图追踪为什么一个对象没有被释放,请使用Instruments中的Leaks工具。如果您想追踪为什么一个对象过早地被释放,请使用Instruments中的Zombies工具。
但不要使用-retainCount,它是一个完全没有用的方法。
编辑:
请大家前往http://bugreport.apple.com并请求将-retainCount弃用。请求越多,效果越好。
第二次编辑:
更新一下,[NSNumber numberWithInt:1]现在的retainCount值为9223372036854775807。如果你的代码期望这个值为2,则你的代码现已失效。

1
retainCount 目前对于单例非常有用。(现在的问题是单例有多有用...)- (NSUInteger)retainCount{return NSUIntegerMax;} - Joe
8
@Joe,你可以创建单例而无需覆盖retainCount方法。 - Dave DeLong
1
一个人也可以不用鸡蛋烤蛋糕。苹果建议覆盖 retainCount 以确保单例在非垃圾回收环境中的状态。http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32 - Joe
5
我知道这个问题;请搜索“objective-c singleton”,查看所有有关为什么该示例不是一个很好的示例的讨论。 - Dave DeLong
2
有一件事情我会用到,@DaveDeLong,你可能也不认同,但我认为这样做是正确的——就是重写dealloc和init方法,并在其中加入NSLog语句(即使使用ARC)。这能让我很好地了解对象是否被正确释放,而这通常是我们试图回答的真正问题。 - Dan Rosenstark
@Yar - 这在 ARC 中可能有些有用,因为 dealloc 方法仍然可以使用,只是用于清理而不是内存管理。在我需要做这个的罕见情况下,我倾向于断点而不是日志记录。 - lxt

51

绝对不要这样做!

认真对待这件事情。只需要遵循内存管理指南,并且只释放你用allocnewcopy(或者任何你最初调用retain的东西)所创建的对象。

@bbum在SO上说得最好,在他的博客中也有更详细的说明。


8
因为你会认为你在计算记忆,但实际上你是错的。 - Abizern
单例的目的是确保对象只有一个实例,不是永远不会被释放。如果在创建单例对象时遵循内存管理规则,它将不会被释放,直到所有保留它的对象都已经释放了它,这也应该是这样的。 - Abizern
3
此外,您可以从Instruments及其工具中获得比 -retainCount 更详细的信息。请注意,这里不包括解释和其他非翻译内容。 - Dave DeLong
3
补充Dave所说的内容;一个对象的绝对引用计数是一个实现细节,既是该对象内部的细节,也可能是该对象传递的任何其他对象的细节。例如,通过UIKit API传递对象后,调用retainretainretainautoreleaseautoreleaseautorelease可能是完全有效的结果。 - bbum
2
如果 retainCount 等于 0,那么奇点已经实现了! - bbum
显示剩余2条评论

15

自动释放对象是一个情况,在这种情况下,检查 -retainCount 是没有信息的,可能会引起误导。保留计数不告诉你关于一个对象被调用了多少次 -autorelease 的信息,因此也不知道当当前自动释放池清空时它将被释放多少次。


1
谢谢 - 这是一个很好的观点。这是第一个回答,除了通用的“不要使用”,还描述了为什么不使用retainCount的原因。 - Moszi
1
@Moszi:你的问题是“何时使用retainCount?”——如果你不是想问这个问题,那你应该问其他的东西。 - Chuck
@chuck - 其实我对“何时使用它”很感兴趣,然而似乎每个答案都会是“永远不使用”…但我想你是对的。我会把这个问题留给几天,如果除了“永远不使用”没有其他答案,那么我会接受“永远不使用”作为答案。 - Moszi

10

我发现使用"Instruments"检查retainCounts非常有用。

在使用"allocations"工具时,确保打开了"Record reference counts"选项,您可以进入任何对象并查看其retainCount历史记录。

通过配对allocs和releases,您可以很好地了解情况,并经常解决那些无法释放的困难情况。

这从未让我失望 - 包括在iOS早期测试版中发现错误。


5
请查看苹果关于NSObject的文档,它几乎涵盖了你的问题:NSObject retainCount
简而言之,如果您没有实现自己的引用计数系统(我几乎可以保证您不会这样做),则retainCount对您来说可能是无用的。
按照苹果公司的说法,“在调试内存管理问题时通常没有价值”。

首先感谢您的回答。通常人们对这个问题的反应是“永远不要”。(请参见答案)。实际上,“调试没有价值”并不意味着“永远不要”。那么我的问题是 - 为什么有强烈的感觉不使用它(例如在单例实现中 - 再次是其中之一的答案)? - Moszi
我猜你需要提供更多关于你使用它的信息。一个可接受的用途是,就像苹果建议的那样,覆盖retainCount来实现自己的引用计数系统。但在实践中,你永远不会看到这种情况(无论如何,我从未见过)。强烈的反应通常来自于苹果自己已经倡导(在他们的邮件组、文档、开发者论坛等)不要去碰retainCount。 - lxt
1
@Moszi:调试是大多数人能够想象它有价值的唯一情况,因此如果在那里不可靠,它永远没有用处。你还考虑过哪些其他用途? - Chuck

4
当然,你在代码中绝不应该使用retainCount方法,因为它的值取决于应用到对象上的自动释放数量,这是无法预测的。然而,在调试过程中非常有用,特别是当你在查找调用Appkit对象方法的代码中的内存泄漏时,而这些方法在主事件循环之外被调用。因此,它不应被弃用。
在你努力表达自己的观点时,你严重夸大了这个值难以理解的本质。所以说它并不总是一个引用计数。有一些特殊的值用于标识,例如指示对象永远不会被释放。像1152921504606846975这样的数字看起来非常神秘,直到你将其用十六进制表示并得出0xfffffffffffffff。9223372036854775807也是0x7fffffffffffffff。使用这些值作为标记并不意外,因为假设每秒增加100,000,000次retainCount,需要近3000年才能得到如此高的retainCount。

3

使用它会有什么问题?它只返回对象的保留计数。我从未调用过它,也想不到任何理由去调用它。但是在单例中,我已经重写了它,以确保它们不会被释放。


2
在单例情况下,没有理由覆盖它。Foundation/CoreFoundation 中没有使用 retainCount 进行内存管理的代码路径。 - bbum
不释放使用它来决定是否调用dealloc吗? - ughoavgfhw
2
不行;retain/release/autorelease 的内部实现深深扎根于 CoreFoundation,与你所期望的完全不同。去获取 CoreFoundation 源代码(它是可用的),看一看吧。 - bbum

3

在你的应用程序启动并执行一些有用的操作之前,不必担心内存泄漏。

一旦应用程序启动后,启动Instruments并使用应用程序,查看是否真的存在内存泄漏。在大多数情况下,您自己创建了一个对象(因此您拥有它),但在完成后忘记释放它。

在编写代码时不要尝试进行优化,当您正常使用应用程序时,您对可能会泄漏内存或需要太长时间的猜测通常是错误的。

请尝试编写正确的代码,例如,如果您使用alloc等创建对象,则确保正确释放它。


0

在你的代码中永远不要使用-retainCount。然而,如果你使用了它,你永远不会看到它返回零。想一想为什么。:-)


-1
Dave在他的帖子中使用的示例是NSNumber和NSStrings...所以,如果您使用其他类,例如UIViews,我相信您将得到正确的答案(保留计数取决于实现方式,并且是可预测的)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接