在Swift中解除分配


145

我想在视图控制器寿命结束时执行一些清除操作,即删除NSNotificationCenter通知。实现dealloc结果会导致Swift编译器错误:

Cannot override 'dealloc' which has been marked unavailable

在Swift中,在对象生命周期结束时执行某些清理的首选方法是什么?

Answers:


333
deinit {
    // perform the deinitialization
}

Swift文档

在取消释放类实例之前,将调用反初始化器。您使用deinit关键字编写反初始化器,类似于使用init关键字编写初始化器。反初始化器仅在类类型上可用。

通常,在实例被释放后,您无需执行手动清理。但是,当您使用自己的资源时,可能需要自己进行一些额外的清理。例如,如果创建一个自定义类来打开文件并向其中写入一些数据,则可能需要在释放该类实例之前关闭该文件。


45
deinit {
    // perform the deinitialization
}

是Swift“ dealloc”的正确答案。

但是,最好指出iOS 9中的新功能,不再需要清理NSNotificationCenter!

https://developer.apple.com/library/content/releasenotes/Foundation/RN-FoundationOlderNotes/index.html#X10_11Notes

NSNotificationCenter

在OS X 10.11和iOS 9.0中,NSNotificationCenter和NSDistributedNotificationCenter将不再向可能释放的已注册观察者发送通知。如果观察者能够存储为弱归零引用,则底层存储会将观察者存储为弱归零引用,或者如果对象不能弱存储(即,它具有自定义的保留/释放机制会阻止运行时) (因为能够弱地存储对象)将其存储为非弱的清零参考。这意味着不需要观察者取消其释放方法的注册。下一个将被发送给该观察者的通知将检测到零引用并自动注销该观察者的注册。如果对象可以被弱引用,则在解除分配期间将不再向观察者发送通知;在非弱地将参考观察者清零的情况下,在重新分配期间接收通知的先前行为仍然存在。当不再使用时,通过-[NSNotificationCenter addObserverForName:object:queue:usingBlock]方法的基于块的观察者仍然需要取消注册,因为系统仍然对这些观察者有很强的引用。仍然支持过早删除观察者(弱引用或归零引用)。CFNotificationCenterAddObserver不符合此行为,因为观察者可能不是对象。当不再使用时,通过-[NSNotificationCenter addObserverForName:object:queue:usingBlock]方法的基于块的观察者仍然需要取消注册,因为系统仍然对这些观察者有很强的引用。仍然支持过早删除观察者(弱引用或归零引用)。CFNotificationCenterAddObserver不符合此行为,因为观察者可能不是对象。当不再使用时,通过-[NSNotificationCenter addObserverForName:object:queue:usingBlock]方法的基于块的观察者仍然需要取消注册,因为系统仍然对这些观察者有很强的引用。仍然支持过早删除观察者(弱引用或归零引用)。CFNotificationCenterAddObserver不符合此行为,因为观察者可能不是对象。

但是请注意以下有关强引用的要点,因此您可能还是要担心清理...?


3
除非通知块具有强引用,否则必须删除观察者。
TigerCoding '16

+1不必清理观察者。重要提示!我使所有捕获参考都变得微弱,因此无需处理。
2013年

2
根据文档,通知块似乎总是被强烈引用。因此:如果您使用块来处理通知,则必须在deinit中为它们注销。
marsbear's

22

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Deinitialization.html

当不再需要实例时,Swift会自动释放其实例,以释放资源。Swift会通过自动引用计数(ARC)处理实例的内存管理,如自动引用计数中所述。通常,在实例被释放后,您无需执行手动清理。但是,当您使用自己的资源时,可能需要自己进行一些额外的清理。例如,如果创建一个自定义类来打开文件并向其中写入一些数据,则可能需要在释放该类实例之前关闭该文件。

每个类定义最多可以有一个反初始化器。反初始化器不接受任何参数,并且写成无括号:

deinit {
    // perform the deinitialization
}

2

在取消分配之前,必须先删除观察者,否则将导致崩溃。可以使用

deinit {
    // perform the deinitialization
    print("deinit")

    removeObserver(self, forKeyPath: kSelectedViewControllerKey, context: nil)
    removeObserver(self, forKeyPath: kSelectedIndexKey, context: nil)

}

-2

deinit调用其他类中的方法时要小心,它可能会导致崩溃


1
否决票不一定是这种情况。从参考。docs由于实例在调用其初始化程序之后才被释放,因此初始化程序可以访问其调用的实例的所有属性,并可以基于这些属性修改其行为(例如查找需要删除的文件名)被关闭)。
superjos
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.