天天看點

KVO remove observer引發的crash

crash日志:

*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <XXXViewController 0x7ff158707f80> for the key path "xxx" from <XXXViewController 0x7ff158707f80> because it is not registered as an observer.'
           

分析原因:在使用KVO時,add observer和remove observer都是配對出現的,首先添加成為觀察者,然後在釋放記憶體的時候移除。例如:

override func viewDidLoad() {
        super.viewDidLoad()
        AppUnreadNumManager.instance.addObserver(self, forKeyPath: "xxx", options: .new, context: nil)
    }
           
deinit {
        AppUnreadNumManager.instance.removeObserver(self, forKeyPath: "xxx", context: nil)
    }
           

通常情況下,這樣寫是沒有問題的,但是有時候控制器可能還沒有加載(viewDidLoad方法沒走),然後就被釋放了(deinit方法走了),這樣就會出現上面那個bug。

我項目中出現這個bug的原因是:我在某個控制器開啟KVO監聽,正好它又是UITabBarController的子控制器,在沒有選中該控制器的情況下,它的viewDidLoad方法是不會走的。如果這時候直接退出UITabBarController,那麼該控制器就會直接被釋放,這樣就會出現還沒添加觀察者就移除觀察者的bug。

解決方案:

var isViewDidLoad = false

    override func viewDidLoad() {
        super.viewDidLoad()
        isViewDidLoad = true
        AppUnreadNumManager.instance.addObserver(self, forKeyPath: "xxx", options: .new, context: nil)
    }
           
deinit {
        if isViewDidLoad {
            AppUnreadNumManager.instance.removeObserver(self, forKeyPath: "xxx", context: nil)
        }
    }