【问题标题】:NSData release is not reclaiming memoryNSData 释放不回收内存
【发布时间】:2010-04-29 20:53:56
【问题描述】:

iPhoneOS 3.2

我使用 NSKeyedUnarchiver 的 unarchiveObjectWithFile: 来加载包含单个大 NSData 和另一个小得多的对象的自定义对象。我的自定义对象中的 dealloc 方法被调用, NSData 对象被释放,它的 retainCount == 1 之前。物理内存不会减少任何数量,更不用说 NSData 大小的一小部分了,并且可以可靠地生成重复内存警告:我进行了测试,直到我实际收到 2 级警告。 =(

NSString *archivePath = [[[NSBundle mainBundle] pathForResource:@"lingering"]
     ofType:@"data"] retain];
lingeringDataContainer = [[NSKeyedUnarchiver unarchiveObjectWithFile:archivePath] retain];
[archivePath release];
[lingeringDataContainer release];

现在是dealloc....

- (void) dealloc {
   [releasingObject release];
   [lingeringData release]; 
   [super dealloc];
}

发布前:

(gdb) p (int) [(NSData *) lingeringData retainCount]
$1 = 1

之后:

(gdb) p (int) [(NSData *) lingeringData retainCount]
目标不响应此消息选择器。

【问题讨论】:

  • 好吧,如果 lingeringData 不再是有效对象,您可能会明白这一点。
  • 我应该明白这一点。它表明我没有在其他地方保留 NSData 对象。

标签: iphone iphone-sdk-3.2


【解决方案1】:

首先,您保留和释放不需要发生这种情况的对象。这是清理后的代码:

NSString *archivePath = [[NSBundle mainBundle] pathForResource:@"lingering"]
     ofType:@"data"]; // Do not retain again.
lingeringDataContainer = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath]; // Do not retain again.
// Do not release, because archivePath is already autoreleaed: [archivePath release];
// Again, this is already autoreleased: [lingeringDataContainer release];

或者更简单地说:

NSString *archivePath = [[NSBundle mainBundle] pathForResource:@"lingering"]
     ofType:@"data"];
lingeringDataContainer = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath]; 

其次,剩下的代码在哪里?它可能是其他东西被保留或缓存在其他地方。

【讨论】:

  • 我需要保留数据容器以备将来使用。再看一下 gdb 输出,你会发现你对任何缓存都是错误的。
  • 我试图编辑上面的评论并且花了太长时间 =( 无论如何,我尝试了你的“清理”代码,因为我相当有信心它不会给出不同的结果,因为保留代码块末尾的计数与我的相同,并且泄漏仍然存在。实际上没有任何其他代码 - 它已被删除以隔离问题。看起来 NSKeyedUnarchiver 中存在错误. 可能类似于:stackoverflow.com/questions/1250050/…
  • 你的 dealloc 应该有一个 [lingeringDataContainer release]。什么是延迟数据?那是一个单独的对象还是一个错字?链接中的问题指向 NSURLConnection。我不知道 NSKeyedArchiver 有任何错误。我经常使用它,但我没有检查它的内存使用情况,所以它可能正在缓存数据。数据的字节大小是多少?
  • 不,不应该。 lingeringData 是一个 NSData...我发布了 lingerDataContainer 的 dealloc。一个对象不会从 dealloc 中调用它自己的释放方法。代码示例很小,但不需要更大,因为数据不用于任何用途。唯一感兴趣的其他代码可能是编码/解码方法......它们非常简单。在我的测试中,字节大小从 1-3mbs 不等。
  • 我刚刚注意到 setObjectZone: 可以由 NSCoder 的子类实现。我很想知道在我尝试之前走这条路线实际上会起作用(销毁每个版本的区域)=)
【解决方案2】:

您何时检查内存使用情况?是在您发布的代码 sn-p 之后吗? archivePathlingeringDataContainer 都是自动释放的。直到(至少)自动释放池被耗尽(通常在当前事件结束时),它们才会被释放。可能还有很多其他内部的东西已经被自动释放并且在池被耗尽之前不会消失。

尝试这样做:

NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; // <==
NSString *archivePath = [[[NSBundle mainBundle] pathForResource:@"lingering"]
 ofType:@"data"] retain];
lingeringDataContainer = [[NSKeyedUnarchiver unarchiveObjectWithFile:archivePath] retain];
[pool release];                                             // <==
[archivePath release];
[lingeringDataContainer release];

【讨论】:

  • 我尝试创建自己的自动释放池来封装取消归档,完成后释放它,并且内存仍然保留。当我重复取消归档对象的过程时,我会使用 Instruments 不断检查内存使用情况。虽然有些人可能会争辩说内存并没有真正被占用,但 1 级和 2 级警告说内存保留是真实的。很有可能 NSKeyedUnarchiver 出于某种原因不使用 iPhoneOS 上的本地自动释放池。 unarchiveObjectWithFile: 是工厂对象调用...
  • 那我就糊涂了。上面的 sn-p 代码不包含任何泄漏,您的原始代码也没有,除非 initWithCoder 中存在泄漏:您可以发布吗?
猜你喜欢
  • 2012-05-22
  • 1970-01-01
  • 2010-12-16
  • 2010-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-29
  • 2015-11-16
相关资源
最近更新 更多