[面试题]自动释放池跟GC有什么区别?iPhone上有GC么?[pool release] 和[pool drain]有什么区别
虽然ARC引入之后NSAutoReleasePool的使用有了很大变化,但是了解NSAutoReleasePool的机制还是十分必要的,下面主要说一下:
一个”Autorelease Pool”实例中“包含”其它各种调用了”autorelease”方法的对象。当它释放时,其中所有被管理对象都会收到”relrease”的消信。注意,同一个对象可以被多次调用”autorelease”方法,并可以放到同一个”Autorelease Pool”中。引入这个自动释放池机制,对象的”autorelease”方法代替”relrease”方法可以延长它的生命周期,直接到当前”Autorelrease Pool”释放。如果想让此对象的生命周期超过”Autorelease Pool”,还可以再次”retain”,呵呵,有意思吧?且让我慢慢道来。
Cocoa总是认为当前至少有一个”Autorelease Pool”对象是可用的。若此对象并不存在,你调用的”autorelease”的所有对象都不会被自动释放掉,可想而知,造成内存泄露。Cocoa把这个错误信息写入日志??仅仅是为了以后分析。
你可以用”alloc”与”init”方法创建一个”NSAutoreleasePool”对象,并且可以调用”release”或”drain”(”release”与”drain”的区别是”drain”在有GC的环境中会引起GC回收操作,”release”反之。但在非GC环境中,两者相同。官方的说法是为了程序的兼容性,应该考虑用”drain”代替”release”,)方法来回收它(调用它的”autorelease”或”retain”方法会引起异常)。在一个完整的上下文最后”Autorelease Pool”对象应该被”release”掉(在方法内或一段循环体内创建的”Autorelease Pool”对象)。
“Autorelease Pools”的所有实例在栈中管理(我们暂时叫他“自动释放池栈”),并且它们是可以被嵌套的(父生子,子生孙。。。子子孙孙 ^_^)。例如,当我们创建一个”Autorelease Pool”对象后,它就被自动放到“自动释放池栈”的栈顶。当本池对象回收时,它就随之从这个栈中POP掉。那么也就是说,当任何一个对象调用”autorelease”方法后,它会被放入当前线程中当前栈顶的自动释放池中。
接下来我们聊聊”Autorelease Pools”的嵌套问题。在你的应用中,你可以任意多的创建”Autorelease Pool”对象,而这些对象被当前线程的“自动释放池栈”所管理。那么除了一个接一个的顺序创建并销毁它的情况外,还有一种使用方式,就是嵌套式的创建与使用。例如:在你的主函数创建了一个”autorelease pool”,然后又调用了创建了”autorelease pool”实例的其它方法;或是在外循环中创建了”Autorelease Pool”的实例,而内循环中也做了相同的事情。有意思吧,呵呵,嵌套的机制使父Pool实例释放后,它的所有子Pool也将释放。但这里还存在一些副作用,后续文章会详细讨论。
“Application kit”在一个事件循环里会自动创建一个”autorelease pool”。像鼠标键的按下与释放,所以你编写的代码通常不需要考虑太多这方面的事情。当然,有以下三种情况你会创建与销毁自己的Pool实例:
1,应用不是基于”Application Kit”,像”Command-line tool”,因为它并没有内置的”autorelease pools”的支持。
2,创建线程,你必需在线程开始时创建一个”Autorelease Pool”实例。反之,会造成内存池露(会在以后的文章详细说明线程与池的技巧)。
3,一个循环内创建了太多的临时对象,你应该为他们创建一个”Autorelease Pool”对象,并在下次循还前销毁它们。
2,自动释放池中的”Non-AppKit”应用
在”Non-AppKit”应用中使用自动释放池的机制其实是相当简单的事情。你仅仅需要在main()起始处创建”Autorelease Pool”对象,并在结尾处释放掉它。就像在Xcode的Foundation Tool的创建模版里写的一样。这个确保你在应用生命周期内至少有一个”Autorelease Pool”是可用的。但是,这也使所有在此期间的所有”autorelease”的对象都必需在应用结束后才被释放。这也许会引起在应用的使用中不断的增长,所以,你仍然考虑在不同的作用域创建新的”Autorelease Pool”。
大多应用中都存在各种级别的循环机制。在这些应用中,你可以在每个循环内的开头创建一个”Autorelease Pool”对象,并在结尾处释放掉它。
例如:
void main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *args = [[NSProcessInfo processInfo] arguments];
unsigned count, limit = [args count];
for (count = 0; count < limit; count++)
{
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSString *fileContents;
NSString *fileName;
fileName = [args objectAtIndex:count];
fileContents = [[[NSString alloc] initWithContentsOfFile:fileName] autorelease];
// this is equivalent to using stringWithContentsOfFile:
[loopPool release];
}
[pool drain];
exit (EXIT_SUCCESS);
}
在命令行中处理所有以参数传来的文件。一次循环处理一个文件。在循环的开头创建一个”NSAutoreleasePool”对象,并在循环结束时释放掉。因此,任何在其中创建并调用“autorelease”的对象都将添加到这个Pool实例中,当本池被释放后,这些对象也将被回收。注意,任何在作用域内创建的”autoreleased”对象(像”fileName”),虽然并没有显示的调用”autorelease”方法,但都将被当前池所管理并释放。