五:Autorelease Pools
这一节相对于上几节,详细讲解了Autorelease Pools
1.Autorelease Pools综述
Autorelease Pools是NSAutorelease的实例,它是一个容器,容纳各种接到autorelease消息的objects。一个object可以被多次放入autorelease pools中,放进去几次就会release几次,不会造成内存泄露。而且,相比较release而言,autorelease延长了object的生命周期。
Cocoa总是相信你有可用的autorelease pool,这也是我们在iOS开发中的main文件中看到autorelease pool的缘由。当然你可以自己alloc一个autorelease pool,并且用drain释放。值得注意的是,不要对autorelease pool发送autorelease或retain或release,而且你drain掉autorelease pool时应该是在同一context(这个词解释不好,还是直接英文,大致就是比如说在同一个函数,同一个循环等)。
Autorelease pools是以栈的形式整理的,而而不是一般认为的内嵌方式。也就是说你新创建的autorelease pool放在栈顶,并且这事你对一个object实行autorelease是把它放在栈顶那个autorelease pool。
为什么autorelease pool被误认为内嵌方式也是情有可原,因为它在外表现形式确实是内嵌,比如你在main文件看到最外层的autorelease pool,当你进入appdelegate时,就可以创建算是内嵌的auotrelease pool。
有三种情况你值得创建自己的autorelease pool:
- 基于Application Kit的程序是自动创建一个autorelease pool的,但如果你写的是像命令行程序那样非基于Application Kit的程序,就要自己创建了。
- 如果你写多线程程序,那么在新开的线程里需要自己写autorelease pool,而且是一开线程就创建,外面那个它不能使用。
- 如果你写了一个循环,这个循环包含大量暂时的object,那么为了内存不告急,你应该在这个循环内创建一个autorelease pool,并把那些暂时的object都扔进去。
2.在不基于AppKit的程序里创建Autorelease Pools
这个还是代码更加直观:
void main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *args = [[NSProcessInfo processInfo] arguments];
for (NSString *fileName in args) {
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSError *error = nil;
NSString *fileContents = [[[NSString alloc] initWithContentsOfFile:fileName
encoding:NSUTF8StringEncoding error:&error] autorelease];
/* Process the string, creating and autoreleasing more objects. */
[loopPool drain];
}
/* Do whatever cleanup is needed. */
[pool drain];
exit (EXIT_SUCCESS);
}
在for循环也创建了一个,减少内存消耗,其他应该没啥可说。
3.Autorelease Pools和多线程
为什么多开的线程不能使用原来就创建的autorelease pools呢?主要是因为,每一个线程都独立得维护着自己特有的autorelease pool堆栈,不同线程间的autorelease pools不能交错使用。如果你新开的线程涉及到大量objects的创建,你值得开一个autorelease pool,而且可以完全仿照AppKit自带的,也不麻烦。
4.始终遵循所有权基本规则
通过autorelease而不是release,使object延长了生命周期。要注意的是,如果一个object已经autorelease了,所有权已经放弃了,就没必要再release了。而且你也不能再去访问它。但有时我们确实需要把autorelease pool范围内的object拉出来在这个autorelease pool范围外使用,你可以retain一下这个object,然后在autorelease pool被drain掉以后,并且使用完这个object后把它autorelease掉,代码如下:
– (id)findMatchingObject:(id)anObject
{
id match = nil;
while (match == nil) {
NSAutoreleasePool *subPool = [[NSAutoreleasePool alloc] init];
/* Do a search that creates a lot of temporary objects. */
match = [self expensiveSearchForObject:anObject];
if (match != nil) {
[match retain]; /* Keep match around. */
}
[subPool drain];
}
return [match autorelease]; /* Let match go and return it. */
}
仔细观察可发现这个match不是在subPool这个autorelease pool,而是在上一级的autorelease pool里了