【问题标题】:How to tell if object is in an NSAutoreleasePool如何判断对象是否在 NSAutoreleasePool 中
【发布时间】:2011-11-22 22:20:14
【问题描述】:

我想知道一个对象被自动释放了多少次。我使用objective c 的时间足够长,通常可以直接知道对象是否已自动释放,但是我经常看到处理内存和保留计数的问题。在某些时候,答案总是会结束,“你不能相信一个对象的retainCount”——我同意,但是如果你能确定一个对象被自动释放多少次,那么你实际上可以信任 如果您添加了以下类别,则为 retainCount:

@interface NSObject (NSObject_MemoryDebugging)
- (NSUInteger) autoReleaseCount;
- (NSUInteger) retainCountWithAutoRelease;
@end

@implementation]
/** Determine how many times this object has been marked for autorelease **/
- (NSUInteger) autoReleaseCount;
{
   // ??? not sure how to figure this out.
   return 0;
}

 - (NSUInteger) retainCountWithAutoRelease
{
   NSUInteger retainCount = [self retainCount];
   NSUInteger autoReleaseCount = [self getAutoReleaseCount];  // ???
   return retainCount - autoReleaseCount;
}
@end

不可变类型仍然会有一个例外,因为它们通常会在复制期间增加保留计数,因此您仍然不能信任这些保留计数。

我不建议什么

我不是在寻找在生产代码中使用 retainCount 的答案。但是,我认为这对于调试内存问题的人来说很有价值。

我想有些人会讨厌这个问题,因为程序员不应该关心对象被自动释放了多少次。编码应该是关于平衡分配、保留、复制、新的发布、故事的结尾。但是,这样做的目的是帮助人们敲打他们的头。 [NSObject retainCount] 烧了很多人,这个问题的答案会很酷。

我确信有一种方法可以确定对象被自动释放的次数。我只是不知道它是什么,因此提出了问题。

查看类似问题:Objects inside NSAutoreleasePool in objective-c

编辑


谢谢大家的回答。你可能会发现这个有趣 => Ariel 指出 GNUStep 的 Cocoa 实现,特别是 NSAutoReleasePool 有这个方法:+(NSUInteger)autoreleaseCountForObject:(id)anObject。此方法很慢,并且仅从调用者线程上的 NSAutoReleasePools 返回自动释放计数。仍然......有趣的是它在那里。文档引用它实际上只对调试有用。这确实是我希望以某种方式在 Cocoa 框架中找到(或找到可能)的东西。

我同意这些答案,即使有可能获得更好的工具(僵尸、泄漏、静态分析器)存在的自动释放计数。

【问题讨论】:

  • Apple 正在将 autorelease 推送到语言/编译器中,任何获取此信息的尝试都将是短暂的 - 搜索 Automatic Reference Counting 以了解详细信息。跨度>
  • 开启NSZombies,你可以随时引用任何被创建的对象——如果它是僵尸,你知道它已经被释放到0
  • 你说,“我认为这对调试内存问题的人来说很有价值。”你能举一个具体的例子说明知道对象的自动释放计数是解决问题的最佳方法吗?我一直在想一个,但我不能。
  • 一个有效且有趣的问题。但最终,答案仍然是“不要打扰,还有更好的工具”。特别是,如果没有完全考虑线程,“......您实际上可以信任retainCount......”的说法是不正确的。同样,框架中的某些类可以直接修改其保留计数而无需调用方法,而其他类与自动释放池一起工作而不会自动释放。

标签: objective-c ios memory-leaks nsautoreleasepool retaincount


【解决方案1】:

首先,您必须处理多个自动释放池,并且一个对象被多次自动释放,可能在多个池中。

其次,不是(只是)NSAutoreleasePool 使 -retainCount 不可信。问题是各种对象,无论是你的还是苹果的,都会出于各种原因保留东西,而你最不知道的。即使考虑到自动释放,您的对象通常也不会具有您期望的保留计数,因为在幕后,有东西正在观察它或暂时将其放入字典中,等等。

调试内存问题的最佳方法是泄漏仪器、NSZombie 和静态分析器。

【讨论】:

  • 有没有什么地方可以让我了解更多有关使retainCount 不可信的其他原因,b/c 我认为自动释放是唯一使它不可信的原因。我知道 CFRetain 和 CFRelease,但我认为这些并没有把 retainCount 扔掉。我还没有尝试过。此外,显然可以列出自动释放池中的所有对象(请参阅下面的答案)。我想有一种方法可以直接查询 NSAutoreleasePools 以获取对象的计数,但是该实现细节隐藏得很好。
  • Apple 在-retainCount 的文档中直接提到了它。除了自动释放,他们还提到“这种方法在调试内存管理问题时通常没有价值。因为任何数量的框架对象都可能保留一个对象以保存对它的引用......”这就是问题所在:太多的目标-C 使用 Cocoa 框架,你不知道 Cocoa 会对你的对象做什么,更不用说它了,就保留/释放而言。并不是-retainCount 不准确,而是它给出的准确值一文不值,因为我们无法理解为什么
【解决方案2】:

不,没有公共 API 来确定对象是否已自动释放。

即使这是公开的,您的-retainCountWithAutoRelease 方法也会有一些问题:

  • 一个对象可以多次放置在同一个自动释放池中,因此您需要一个自动释放池的自动释放计数,而不是指示对象是否已自动释放的标志;

  • 由于多线程,一个对象可以放置在多个自动释放池中,因此您需要跨多个自动释放池的自动释放计数;

  • 由于多线程,您需要将代码与 Cocoa 对保留计数和自动释放池的处理同步,并且 Cocoa 使用的内部锁定对应用程序不可见。

【讨论】:

  • 看起来 GNUStep 的 NSAutoReleasePool 实现有 +(NSUInteger)autoreleaseCountForObject:(id)anObject。如果您有兴趣,请参阅GNUStep NSAutoReleasePool docs。它有你提到的问题。它很慢,只计算当前线程中来自 NSAutoReleasePools 的自动释放。仍然......有趣的是他们提供它。 Ariel 让我在他的回答中查看 GNUStep 文档。
【解决方案3】:

听起来您需要重写 -(id)autorelease; 方法,因为在那里完成了将对象添加到 NSAutoreleasePool 的工作。
类似的事情:

-(id)autorelease{
    _isAutoreleased = YES;  //some BOOL member initialized to NO and returned on -(BOOL)isAutoreleased;
    [NSAutoreleasePool  addObject:self];
    return self;
}

也可以看看this link

【讨论】:

  • 我看到您已链接到 GNUStep 文档。 GNUStep 的做事方式可能与 Cocoa 不同。如果你想覆盖autorelease,返回[super autorelease] 似乎是一个更安全的计划。
  • @Ariel +1 GNUStep 有一个方法+autoreleaseCountForObject:(id)anObject 真的很有趣。上面的文档特别说它很慢,只返回当前线程上 nsautorelease 池的计数,主要用于调试。我确信这样的东西很容易存在于 Cocoa NSAutoReleasePool 中。
  • @JoshCaswell 不幸的是,你不能通过调用 super 来获得你想要的东西,因为我们正在讨论类的类别扩展而不是覆盖一个......我唯一忘记的想法是在调用 +(void)addObject:(NSObject)object 之后释放 self ; NSAutoreleasePool 类的类方法
  • @Ariel 您可以使用method swizzling 调用您已覆盖的基类方法(如本例中的自动释放)。我尝试覆盖自动释放,它实际上工作得非常好。我没有对它进行详尽的测试,为了让它工作,我使用了一个静态 NSMutableDictionary,其中对象是键,值是包含自动释放计数的 NSNumber。但是,这会为添加到字典中的对象添加保留计数,这最终会产生问题。好主意!
  • 子类继承协议和其他方法一样;你当然可以使用[super overriddenProtocolMethod] 调用超类的实现。
【解决方案4】:

NSAutoreleasePool 确实有一个我以前完全不知道的+(void)showPools 方法。这可能很有用。另见Is there a way to inspect an NSAutoreleasePool's objects?。在那个帖子上,KennyTM 说(在评论中):

好吧,既然内容被打印到 stderr,你可以重新打开 流并从中解析以获取所有指针

作为参考,我对 Foundation 框架使用了 class-dump 来查看它的 NSAutoreleasePool 详细信息,它具有以下内容:

@interface NSAutoreleasePool : NSObject {
    void *_token;
    void *_reserved3;
    void *_reserved2;
    void *_reserved;
}

+ (void)addObject:(id)arg1;
+ (id)allocWithZone:(struct _NSZone *)arg1;
+ (void)showPools;
+ (void)releaseAllPools;
+ (unsigned int)autoreleasedObjectCount;
+ (unsigned int)topAutoreleasePoolCount;
+ (BOOL)autoreleasePoolExists;
+ (void)enableRelease:(BOOL)arg1;
+ (void)enableFreedObjectCheck:(BOOL)arg1;
+ (unsigned int)poolCountHighWaterMark;
+ (void)setPoolCountHighWaterMark:(unsigned int)arg1;
+ (unsigned int)poolCountHighWaterResolution;
+ (void)setPoolCountHighWaterResolution:(unsigned int)arg1;
+ (unsigned int)totalAutoreleasedObjects;
+ (void)resetTotalAutoreleasedObjects;
- (id)init;
- (void)drain;
- (oneway void)release;
- (id)initWithCapacity:(unsigned int)arg1;
- (void)addObject:(id)arg1;
- (id)retain;
- (unsigned int)retainCount;
- (id)autorelease;
- (void)dealloc;
@end

我已将其添加为答案,因为它似乎更像是一个答案,而不是我提出的问题的更多细节。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-28
    • 1970-01-01
    相关资源
    最近更新 更多