对象在经历其生命周期后,最终会为系统回收,这时就要执行dealloc方法了。在每个对象的生命期内,此方法仅执行一次,也就会说当保留计数降为0的时候。然而具体何时调用,则无法保证。你绝不应该自己调用dealloc方法。运行期系统会在适当的时候调用它。
那么应该在dealloc方法中做些什么呢?主要就是释放对象所拥有的引用,也就是把所有Objective-C对象都是放掉,ARC会通过自动生成.cxx_destruce方法在dealloc中为你自动添加这些释放代码。对象所拥有的其他非Objective-C对象也要释放。
在dealloc方法中,通常还要做一件事,那就是爸原来配置过的观测行为(observation behavior)都清理掉。如果用NSNotificationCenter给此对象订阅(register)过某种通知,那么一般应该在这里注销(unregister),这样的话,通知系统就不再把通知发给回收后的对象了。
虽说应该于dealloc中释放引用,但是开销大或系统内稀缺的资源则不在此列。像是文件描述(file description)、套接字(socket)、大块内存等,都属于这种资源。不能指望dealloc方法必定会在某个特定的时机调用,因为有一些无法预料的东西可能也持有此对象。如果非要等到系统调用dealloc方法时才释放,那么保留这些稀缺资源的时间就有些过长了,这么做不合适。通常的做法是,实现另外一个方法,当应用程序用完资源对象后,,就调用此方法。这样一来,资源对象的生命期就变得更为明确了。
在清理方法而非dealloc方法中清理资源还有个原因,就是系统并不保证每个创建出来的对象的dealloc都会执行。极个别情况下,当应用程序终止时,仍有对象处于存活状态,这些对象没有收到dealloc消息。在Mac OS X及iOS应用程序所对应的application delegate中,都含有一个会于程序终止时调用的方法。如果一定要清理某些对象,那么可在此方法中调用那些对象的“清理方法”。
Mac OS X系统里,应用程序终止时会调用NSApplicationDelegate之中的下列方法:
- (void)applicationWillTerminate:(NSNotification *)notification
而在iOS系统里,应用程序终止时则会调用UIApplicationDelegate中的下述方法:
- (void)applicationWillTerminate:(UIApplication *)application
编写dealloc方法时还需注意,不要在里面随便调用其他方法。如果在这里调用的方法又要异步执行某些任务,或是又要继续调用他们自己的某些方法,那么等到那些任务执行完毕时,系统已经把当前这个待回收的对象彻底摧毁了。
请再注意一个问题:调用dealloc方法的那个线程会执行“最终的释放操作”(final release),令对象的保留计数降为0,而某些方法必须在特性的线程里(比如主线程)调用才行。若在dealloc里调用了那些方法,则无法保证当前这个线程就是那些方法所需要的线程。
在dealloc里也不要调用属性的存取方法,因为有人可能会覆写这些方法,并于其中做一些无法在回首阶段安全之行的操作。此外,属性可能正处于“键值观测”(Key-Value Observation, KVO)机制的监控之下,该属性的观察者(observer)可能会在属性值改变时“保留”或使用这个即将回收的对象。
要点###
- 在dealloc方法里,应该做的事情就是释放指向其他对象的引用,并取消原来订阅的“键值观测”(KVO)或NSNotificationCenter等通知,不要做其他事情。
- 如果对象持有文件描述符等系统资源,那么应该专门写一个方法来释放此种资源。这样的类要和其使用者约定:用完资源后必须调用close方法。
- 执行异步任务的方法不应该在dealloc里调用;只能在正常状态下执行的那些方法也不应在dealloc里调用,因为此时对象已处于正在回收的状态了。
文/z_zero(简书作者)原文链接:http://www.jianshu.com/p/8829f7a09475著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。