返回参数对象时 ARC 的内存泄漏
Posted
技术标签:
【中文标题】返回参数对象时 ARC 的内存泄漏【英文标题】:Memory leak with ARC when returning a parameter object 【发布时间】:2012-05-26 15:35:24 【问题描述】:使用 ARC(自动引用计数)时,我遇到了一个奇怪(在我看来)和简单的内存泄漏情况。我正在使用 ios 代码,但我认为它应该普遍适用于 Objective-C。
我有一个以下函数,它在检查参数对象的属性后返回作为参数分配的对象。
- (id) returnParam:(id) obj
// Do whatever filtering needed. Return nil for invalid input.
return (NSNumber *)obj;
如果我在循环中调用这个方法如下,我可以看到分配的内存一直在增加,直到循环在 Instruments 中结束。
for(int i = 0; i < 1000000; i++)
id obj = [[NSNumber alloc] initWithInt:i];
id obj2 = [self returnParam:obj];
NSLog(@"attempt %@", obj2);
但是,如果我将returnParam
函数的内容按如下方式放入循环中,则一切正常。内存脚印一直保持相同的大小。
for(int i = 0; i < 1000000; i++)
id obj = [[NSNumber alloc] initWithInt:i];
// Do whatever filtering needed. Break on invalid input.
id obj2 = obj;
NSLog(@"attempt %@", obj2);
我已经消除了过滤部分(所以本质上该函数只将对象传递回调用者),同样的情况仍然存在。
不明白为什么这个序列没有按预期减少保留计数,我在这里和那里尝试了__weak
、__unsafe_unretained
的所有可能组合,但没有一个有效。
有人可以解释为什么这个(返回参数对象)不起作用,并建议我解决这个问题吗?
附:顺便说一句,它在 Instruments 中没有被捕获为内存泄漏事件,但我认为的情况是明显的内存泄漏。
【问题讨论】:
【参考方案1】:这不是泄漏。这与-autorelease
的工作方式有关。扩展 -returnParam:
的作用:
-(id) returnParam:(id) paramToReturn
// make sure the variable is in the scope for the entirety of the function
id returnValue = nil;
@try
// when the parameter is passed in, ARC will automatically retain it.
[paramToReturn retain];
returnValue = [paramToReturn retain]; // strong assignment (a = b)
return returnValue;
@finally
[paramToReturn release]; // release the value we retained when we passed in the parameter
[returnValue autorelease]; // autorelease the return value so it is still valid after the function exits.
现在,让我们将其与您的其他循环进行比较:
for (int i = 0; i < 1000; i++)
id obj = [[NSNumber numberWithInt:i] retain]; // retain, because 'obj' is strong by default.
id obj2 = [obj retain]; // retain, because 'obj2' is strong by default
NSLog(@"attempt %@", obj2);
[obj release]; // release because 'obj' is strong
[obj2 release]; // release because 'obj2' is strong.
因此,在弹出下一个 autorelasepool 之前,您的变量不会被清理,这通常发生在 iPhone 应用程序中的下一个 NSRunLoop
滴答声中,或者可能发生在控制台应用程序中 @autoreleasepool
的末尾。
【讨论】:
您在 Instruments 中尝试过上述代码吗?如果打开优化器时代码“正常工作”,我不会感到惊讶,只要方法和循环都是 ARC 的。如果它仍然在自动释放池中增加内存,我也不会感到惊讶...... @bbum 不是我的回答所说的吗?Thus, your variables aren't getting cleaned up until the next autorelasepool is popped...
@Richard 这是有道理的。我不理解(或没有深入思考)ARC 将通过调用autorelease
返回对象。因此,当我有huge
循环时,这可能表明我不应该调用返回对象(不一定是参数对象)的函数。或者您知道任何解决方法可以使这项工作发挥作用吗?
@barley 在循环内部创建自己的@autoreleasepool
。在完成主要解析的情况下,这是一种相当常见的解决方案,并且可以显着提高性能和内存占用。
我的建议是,如果启用优化,这实际上可能像 OP 所写的那样“正常工作”,但如果没有启用,则会失败。 IE。当从 ARC 代码调用到 ARC 代码时,编译器会优化对保留/释放/自动释放的调用(实际上它会消除自动释放)。但这可能是特定于优化器的问题,当然,出于调试目的,自动释放池可能仍然是必需的。以上是关于返回参数对象时 ARC 的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章