Retain & Release
在Objective-C中,每一个对象都有一个引用计数值(reference count)。对于新创建的对象其初始值为1,向一个对象发送retain消息会让它的引用计数加1;而向其发送release消息则会对让引用计数减1。如果引用计数爲1時被release,则这个对象会被发送一个dealloc消息进行销毁, 所以也不會存在计数为0的对象。
所以一般在Objective-C中如果你alloc,retain或者copy了一个对象,那么你就应该负责release该对象。但这里也有一个问题,就是我们有时无法预知何时释放对象(比如说需要返回一个对象时),如果过早释放,则对象无法被继续使用,如果没有释放,则可能会造成内存泄露。于是Foundation中提供了NSAutoreleasePool作为解决方案。
NSAutoreleasePool
autorelease这个方法是在NSObject中实现的,它所做的事情就是调用
[NSAutoreleasePool addObject:self];
由于addObject是一个类方法,所以它需要查找对应的实例去执行操作。 NSAutoreleasePool的实例保存在每个线程中一个特定的栈里,释放池创建时会被压入该栈中,而当销毁时它会被弹出栈。当需要获取当前的释放池时,NSAutoreleasePool就会从当前的栈顶取出释放池进行操作。找到对应的释放池后addObject方法会将需要自动释放的对象加入到释放池对象的一个列表中。最后,当释放池将被销毁时,池中的每个对象就会被发送一个release消息。如果被销毁的释放池不位于栈的顶部,那么位于它之上的所有释放池也会被销毁。
在引用计数的环境下,Cocoa希望总是有一个自动释放池可用。如果没有自动释放池可以使用,你放入自动释放池中的对象得不到释放,产生了内存泄漏。在这种情况下你的程序应该给与相应的警告日志提醒。
应用程序框架会在每次主线程驱动事件循环开始时创建一个自动释放池。当这个事件循环结束时,会释放掉这个事件循环过程中产生的自动释放对象。所以我们基本上不用自己去创建自动释放池。如果你在程序中的某段代码快内创建了大量的自动释放的临时对象,那么你应该在代码快的开始创建自己的自动释放池。
什么是事件循环呢?这里引用Apple文档里面的一句话:
An event loop is simply a run loop: an event-processing loop for scheduling work and coordinating the receipt of events from various input sources attached to the run loop.
可以是NSTimer的一次回调过程,或者某个iphone上的触摸事件,异步http连接下当接受完数据时,甚至是程序代理类控制的各个过程,比如加载完成时,即将结束时的函数调用。
每个线程(包含主线程)都有一个管理NSAutoreleasePool实例的栈。当自动释放池创建后就会被压入栈中,当释放时就会从栈顶弹出。当线程终止时,所有在该线程栈中的NSAutoreleasePool实例都会被释放。