これまで、NotificationCenterのaddObserverはSwiftでもaddObserver(_:selector:name:object:)で書いていたのだけど、ふと「あー@objc書きたくないな〜」と思った。
そこで単純にaddObserver(forName:object:queue:using:)に置き換えたのだけど、これでリークを作ってしまった。
ようは、元々Objective-Cのセレクタを呼び出すこういうコードがあって
class TestViewController: UIViewController { required init?(coder: NSCoder) { super.init(coder: coder) NotificationCenter.default.addObserver(self, selector: #selector(onAlpha(_:)), name: .myAppAlphaActionNotificaiton, object: nil) } deinit { NotificationCenter.default.removeObserver(self) } @objc func onAlpha(_ notificaiton: Notification) { // any } }
こんな感じでaddObserver(forName:object:queue:using:)
に書き換えたところ、このViewControllerをNavigationControllerからpopしてもdeinitされなくなった。
class TestViewController: UIViewController { private var alphaNotificationHandle: AnyObject? required init?(coder: NSCoder) { super.init(coder: coder) alphaNotificationHandle = NotificationCenter.default.addObserver( forName: .myAppAlphaActionNotificaiton, object: nil, queue: OperationQueue.main, using: onAlpha(_:) ) } deinit { alphaNotificationHandle = nil } func onAlpha(_ notificaiton: Notification) { // any } }
よく考えたら、そのonAlpha
の参照を残すために強参照が作られてしまって解放されなくなるのが原因だとわかるけど単純に書き換えでオッケーだなって軽く思っていて痛い目を見た感じ。
こう書いてある箇所では問題なかった。
class TestViewController: UIViewController { private var alphaNotificationHandle: AnyObject? required init?(coder: NSCoder) { super.init(coder: coder) alphaNotificationHandle = NotificationCenter.default.addObserver( forName: .myAppAlphaActionNotificaiton, object: nil, queue: OperationQueue.main ) {[weak self] notification in self?.onAlpha(notification) } } deinit { alphaNotificationHandle = nil } func onAlpha(_ notificaiton: Notification) { // any } }
addObserver(forName:object:queue:using:)
のusingは、ブロックを渡すのみで運用した方が良さそう。同様に、パラメータが一緒だからといって、引数にインスタンスメソッドそのものを渡すのは避けておいた方がトラブル回避できて良さそうだなと思った。