内存管理
重要笔记
- ARC中不捕获异常的原因!
- MRC中不捕获异常的原因!
第 29 条 理解引用计数
引用计数的工作原理
在引用计数的架构下,对象有个计数器,用以表示当前有多少个事物想令此对象继续存活下去。这在Objective-C中就叫做“保留计数”。
NSObject协议声明了三个方法用以操作计数器
- Retain
- release
- autorelease
第 31 条 在delloc方法中只释放引用并解除监听
绝不能自己调用delloc
应该在delloc中做些什么呢?
- 主要就是释放对象所拥有的引用,也就是把所有Objective-C对象都释放掉,ARC会通过自动生成的.cxx_destruct方法,在delloc中为你自动添加这些释放代码。对象所拥有的其他非Objective-C对象也要释放。比如CoreFoundation对象就必须手工释放,因为他们都是由纯C的API生成的。
- 清理观察则
-(void)delloc {
CFRelase(coreFoundationObject);
[[NSNotificationCenter defaultCenter] removeObserve:self];
}
如果是MRC中需要调用 [super delloc], ARC会自动执行此操作。
delloc注意事项
-
虽说应该与delloc中释放引用,但是开销较大或系统内稀缺的字眼则不在此列。像是文件描述(file descriptor)、套接字(socket)、大块内存等,都属于这种资源。不能指望delloc方法必定会在某个特定的时机随机调用,因为有一些无法预料的东西也持有此对象,在这种情况下,如果非等到系统调用delloc方法是才释放,那么保留这些需缺资源的时间就有写过长了,这么做不合适。通常的做法是,实现另一个方法,当应用程序用完资源对象后,就调用此方法,这样一来,资源对象的生命周期就变的更为明确了。
-
在极个别情况下,当应用程序终止时,仍有对象处于存活状态,这些对象没有收到delloc消息。由于应用程序终止之后,其占用的资源也会返还给操作系统,所以实际上这些对象也就等于消亡了。不调用dealloc方法是为了优化程序效率。而这也说明系统未必会在每个对象上调用delloc方法。
-
如果对象管理者某些资源,那么在dealloc中也要调用“清理方法”,以防开发者忘了清理这些资源。忘记清理资源的情况经常发生,所以最好能输出一行消息,提示程序员代码里含有变成错误。在系统回收对象之前,必须调用close以释放其资源,都则close方法就失去意义了,因此,没有适时调用close方法就是编程错误。输出错误消息可促进开发者纠正次问题。而且,在程序员忘记调用close的情况下,我们应该在dealloc中补上这次调用,以防泄漏内存。如下:
-(void)close { /* clean up resourse */ _closed = YES } -(void)delloc { if (!_closed) { NSLog(@"ERROR: close was not called before dealloc"); [self close]; // 调用这句话是为了侦测变成错误而破例,无论这里调 用什么方法都不太应该。如果在这里所调用的方法又要异步执行某些任务, 或是又要继续调用他们自己的某些方法,那么等到那些任务执行完毕时,系 统已经把当前这个带回收的对象彻底摧毁了。这回导致很多问题,且经常使 应用程序崩溃,因为那些任务执行完毕后,要回调此对象,告诉该对象任务 已完成,而此时如果对象已摧毁,那么回调操作就会出错。 } }
-
调用delloc方法的那个线程会执行“最终的释放操作”(final release),令对象的保留计数将为0,而某些方法必须在特定的线程里(比如主线程)调用才行。若在delloc里调用了哪些方法,则无法保证当前这个线程就是哪些方法所需要的线程。通过编写常规代码的方式,无论如何都没办法保证其会安全运行在正确的线程上,因为对象处于“正在回收的状态”(dellocating state),为了指明此状况,运行期系统已经改动了对象内部的数据结构。
-
在delloc里也不要调用属性的存取方法,因为有人可能会覆写这些方法,并与其中做一些无法再回收阶段安全执行的操作。此外,属性可能正处于“简直观测”(Key-Value Observation, KVO)机制的监控之下,该属性的观察者(observer)可能会在属性值改变时“保留”或使用这个即将回收的对象。这种做法会令运行期希同的状态完全失调,从而导致一些莫名其妙的错误。
要点
- 在delloc方法里,应该做的事情就是释放指向其他对象的引用,并取消原来订阅的“简直观测”(KVO)或NSNotificationCenter等通知,不要做其他事情。
- 如果对象持有文件描述符等系统资源,那么应该专门编写一个方法来释放此种资源。这样的类药喝起使用者约定:用完资源后必须调用close方法。
- 执行异步任务的方法不应在dealloc里调用:只能在正常状态下执行的那些方法也不应在delloc里调用,因为此对象已处于正在回收的状态了。