我一直在查看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大师可以对此有所了解吗?
答案 0 :(得分:12)
恕我直言,哪种方式'正确'是一个偏好的问题。我并不反对主张不使用autorelease
的响应者,但我倾向于使用autorelease
,除非有绝对令人信服的理由不这样做。我会列出我的理由,你可以决定它们是否适合你的编程风格。
正如查克所指出的,有一个半城市的传说,使用自动释放池有一些开销。这可能不是事实,这来自于使用Shark.app花费无数个小时来挤出代码中的最后一点性能。试图对此进行优化是“过早优化”领域的深层原因。如果且仅当Shark.app为您提供硬数据时,您可能会考虑这个问题。
正如其他人指出的那样,一个自动释放的对象“在稍后发布”。这意味着他们徘徊在周围,占据记忆,直到“后来的点”滚动。对于“大多数”情况,这是在运行循环休眠直到下一个事件(计时器,用户点击某些内容等)之前的事件处理过程的底部。
但有时,您需要尽快摆脱这些临时对象,而不是稍后。例如,您需要处理一个巨大的,多兆字节的文件,或数据库中的数万行。发生这种情况时,您需要在精心挑选的点放置NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
,然后在底部放置[pool release];
。这几乎总是发生在某种“循环批处理”中,因此它通常位于某个关键循环的开始和底部。同样,这应该是基于证据的,而不是基于预感的。 Instrument.app的ObjectAlloc是您用来查找这些问题点的方法。
我更喜欢autorelease
到release
的主要原因是,很多更容易编写无泄漏程序。简而言之,如果您选择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
时,将抛出异常,并且您永远不会发送obj
其release
消息。或者在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(更新:事实证明问题出现在我的代码的其他地方,但仍然存在与自动释放行为的细微差别......)