在将对象添加到集合之前使用自动释放?

时间:2009-07-18 15:20:10

标签: iphone objective-c memory-management autorelease

我一直在查看StackOverflow上提出的问题,但在Objective-C中有很多关于内存管理的内容,我找不到我想要的答案。

问题是,在将新创建的对象添加到集合(如NSMutableArray)之前,是否可以(并建议)调用autorelease?或者我应该在添加后明确地发布它。 (我知道NSMutableArray会保留对象)

这说明了我的问题:

情景A(自动释放):

- (void) add {
   // array is an instance of NSMutableArray

   MyClass *obj = [[[MyClass alloc] init] autorelease];

   [array addObject:obj];
}

场景B(明确发布):

- (void) add {
   // array is an instance of NSMutableArray

   MyClass *obj = [[MyClass alloc] init];

   [array addObject:obj];

   [obj release];
}

我认为两者都是正确的,但我不确定,我肯定不知道优先的方式是什么。

Objective-C大师可以对此有所了解吗?

7 个答案:

答案 0 :(得分:12)

恕我直言,哪种方式'正确'是一个偏好的问题。我并不反对主张不使用autorelease的响应者,但我倾向于使用autorelease,除非有绝对令人信服的理由不这样做。我会列出我的理由,你可以决定它们是否适合你的编程风格。

正如查克所指出的,有一个半城市的传说,使用自动释放池有一些开销。这可能不是事实,这来自于使用Shark.app花费无数个小时来挤出代码中的最后一点性能。试图对此进行优化是“过早优化”领域的深层原因。如果且仅当Shark.app为您提供硬数据时,您可能会考虑这个问题。

正如其他人指出的那样,一个自动释放的对象“在稍后发布”。这意味着他们徘徊在周围,占据记忆,直到“后来的点”滚动。对于“大多数”情况,这是在运行循环休眠直到下一个事件(计时器,用户点击某些内容等)之前的事件处理过程的底部。

但有时,您需要尽快摆脱这些临时对象,而不是稍后。例如,您需要处理一个巨大的,多兆字节的文件,或数据库中的数万行。发生这种情况时,您需要在精心挑选的点放置NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];,然后在底部放置[pool release];。这几乎总是发生在某种“循环批处理”中,因此它通常位于某个关键循环的开始和底部。同样,这应该是基于证据的,而不是基于预感的。 Instrument.app的ObjectAlloc是您用来查找这些问题点的方法。

我更喜欢autoreleaserelease的主要原因是,很多更容易编写无泄漏程序。简而言之,如果您选择release路线,则需要保证 {b} release最终会发送到obj所有 的情况。虽然这看起来很简单,但实际上却很难做到。以你的例子为例:

   // array is an instance of NSMutableArray
   MyClass *obj = [[MyClass alloc] init];
   [array addObject:obj];
   // Assume a few more lines of work....
   [obj release];

现在想象一下,出于某种原因,某些地方,巧妙地违反了array可变的假设,可能是使用某种方法处理结果的结果,并且创建了包含处理结果的返回数组作为NSArray。当您将addObject:发送到该不可变NSArray时,将抛出异常,并且您永远不会发送objrelease消息。或者在obj alloc d和必需调用release之间出现问题,就像你立即检查一些条件和return()一样这是错误的,因为它让你忘记了之后发送release的电话必须

你刚刚泄露了一个物体。并且可能已经签了几天试图找出泄漏的地方和原因。根据经验,您将花费很多时间查看上面的代码,确信它不可能成为泄漏源,因为您非常清楚地发送obj release。然后,几天之后,你将体验到只能被描述为宗教顿悟的东西,因为你对问题的原因有所启发。

考虑autorelease案例:

   // array is an instance of NSMutableArray
   MyClass *obj = [[[MyClass alloc] init] autorelease];
   [array addObject:obj];
   // Assume a few more lines of work....

现在,发生的事情已经不再重要了,因为即使在非常不寻常或特殊的极端情况下,它几乎不可能意外泄漏obj

答案 1 :(得分:7)

两者都是正确的,并且会像你期望的那样工作。

我个人更喜欢使用后一种方法,但仅仅因为我喜欢明确何时释放对象。通过自动释放对象,我们所做的只是说“这个对象将在未来的某个任意点发布。”这意味着您可以将自动释放的对象放入数组中,销毁数组,并且对象可能(可能)仍然存在。

使用后一种方法,对象将立即被阵列销毁(假设没有其他任何东西出现并在此期间保留它)。如果我在一个内存受限的环境(比如iPhone)中我需要注意我正在使用多少内存,我将使用后一种方法,因此我没有那么多的对象挥之不去一个NSAutoreleasePool。如果内存使用对你来说不是一个大问题(通常也不适合我),那么任何一种方法都是完全可以接受的。

答案 2 :(得分:3)

它们都是正确的但B可能是首选,因为它根本没有开销。自动释放导致自动释放池负责对象。这有一个非常小的开销,当然,它会乘以所涉及的对象数量。

因此,对于一个对象A和B或多或少相同,但绝对不要在具有大量对象的场景中使用A来添加到数组中。

在不同情况下,自动释放可能会延迟并累积在线程末尾释放许多对象。这可能是次优的。在没有明确干预的情况下,无论如何都要注意自动释放​​。例如,许多getter以这种方式实现:

return [[myObject retain] autorelease];

因此,无论何时调用getter,都会将对象添加到自动释放池中。

答案 3 :(得分:0)

您可以随时发送autorelease消息,因为在应用程序的消息循环重复之前(即在您的所有方法都响应用户输入完成所有方法之前),它才会被执行。

http://macdevcenter.com/pub/a/mac/2001/07/27/cocoa.html?page=last&x-showcontent=text

答案 4 :(得分:0)

你有alloc'对象,那么你的工作就是在某个时候释放它。两个代码段的工作方式都是相同的,正确的方式,autorelease方式可能会慢一些。

就个人而言,我更喜欢autorelease方式,因为它更容易打字,几乎从不是瓶颈。

答案 5 :(得分:0)

他们都很好。有些人会因为“开销”或某些事情而告诉你要避免自动释放,但事实是,实际上没有开销。继续进行基准测试并尝试找到“开销”。你避免使用它的唯一原因是像iPhone一样处于记忆匮乏的境地。在OS X上,你几乎拥有无限的内存,所以它不会产生太大的影响。只需使用最方便的选择。

答案 6 :(得分:0)

我更喜欢A(自动释放)以简洁和“安全”,正如johne所说的那样。它简化了我的代码,我从来没有遇到过它的问题。

也就是说,直到今天:在将块添加到数组之前,我遇到了自动释放块的问题。请参阅我的stackoverflow问题: [myArray addObject:[[objcBlock copy] autorelease]] crashes on dealloc'ing the array(更新:事实证明问题出现在我的代码的其他地方,但仍然存在与自动释放行为的细微差别......)