8.1: kvo 與 kvc 展開(kāi)
? ? ?1:KVO
? ? ? ? ? ? KVO(Key-Value-Observing)鍵值觀察,其技術(shù)原理就是通過(guò) isa waizzle 技術(shù)添加被觀察對(duì)象中間類,并重新寫相應(yīng)的方法來(lái)監(jiān)聽(tīng)鍵值變化。當(dāng)被觀察的對(duì)象屬性被修改后,則對(duì)象回接收到通知,即每次指定的被觀察對(duì)象的屬性被修改后,kvo就會(huì)自動(dòng)通知相應(yīng)的觀察者。
? ? ? ? ? ? isa swizzle 不同于method swizzle,其交換的是isa,對(duì)象的isa 指針式定義了它的類,所以ISA swizzling 指修改對(duì)象所指向的類,KVO則是使用該技術(shù)實(shí)現(xiàn)的,還有zombie objects檢測(cè)也用到了該技術(shù),而method swizzle交換的是method
? ? ? ? 2:KVO引起的crash 情況如下
? ? ? ? ? ? 2.1* observer 已銷毀,但是未及時(shí)移除監(jiān)聽(tīng);
? ? ? ? ? ? 2.2* addObserver 與 removeObserver 不匹配
? ? ? ? ? ? ? ? 1:移除了未注冊(cè)的觀察者,但是未及時(shí)移除監(jiān)聽(tīng)
? ? ? ? ? ? ? ? 2:重復(fù)移除多次,移除次數(shù)多于添加次數(shù),導(dǎo)致崩潰。
? ? ? ? ? ? ? ? 3:重復(fù)添加多次,雖然不會(huì)崩潰,但是發(fā)生改變時(shí),也同時(shí)會(huì)被觀察多次。
? ? ? ? ? ? 2.3*添加了觀察者,但未實(shí)現(xiàn)observerValueForKeyPath:ofObject:change:context: 方法,導(dǎo)致崩潰.
? ? ? ? ? ? 2.4*添加或移除時(shí) keypath == nil ,導(dǎo)致崩潰.
? ? ? ? 通過(guò)如上場(chǎng)景就可以發(fā)現(xiàn)其實(shí)kvo 崩潰的主要原因是觀察者管理混亂,特別是觀察者關(guān)系復(fù)雜時(shí),開(kāi)發(fā)者容易導(dǎo)致混亂。
? ? ? ? ? ? 如下圖所示:

? ? ? ? 那如何管理呢? 既然觀察者都是開(kāi)發(fā)者來(lái)管理,由人來(lái)管理必然會(huì)出現(xiàn)失誤的時(shí)候,那我們是否能通過(guò)一個(gè)代理對(duì)象來(lái)管理?
? ? ? ? ? ? 答案:yes!

? ? ? ? 3:具體實(shí)現(xiàn)如下:
? ? ? ? ? ? 1:通過(guò)Method Swizzle方法調(diào)配交換KVO相應(yīng)的方法到NSObject基類,如下:

? ? ? ? ? ? 2: 然后在觀察者和被觀察者之間建立一個(gè) KVODelegate 對(duì)象,
? ? ? ? ? ? ? ? 兩者之間通過(guò) KVODelegate 對(duì)象 建立聯(lián)系。然后在添加和移除操作時(shí),
? ? ? ? ? ? ? ? 將 KVO 的相關(guān)信息例如 observer、keyPath、options、context 保存為 KVOInfo 對(duì)象,并添加到 KVODelegate 對(duì)象 中對(duì)應(yīng) 的 關(guān)系哈希表 中,對(duì)應(yīng)原有的添加觀察者。
? ? ? ? ? ? 3: 在添加和移除操作的時(shí)候,
? ? ? ? ? ? ? ? 利用 KVODelegate 對(duì)象 做轉(zhuǎn)發(fā),
? ? ? ? ? ? ? ? 把真正的觀察者變?yōu)?KVODelegate 對(duì)象,
? ? ? ? ? ? ? ? 而當(dāng)被觀察者的特定屬性發(fā)生了改變(會(huì)被調(diào)用到observeValueForKeyPath:ofObject),
? ? ? ? ? ? ? ? 再由 KVODelegate 對(duì)象分發(fā)到原有的觀察者上。
? ? ? ? ? ? 4:為了避免被觀察者提前被釋放,
? ? ? ? ? ? ? ? 被觀察者在 dealloc 時(shí)仍然注冊(cè)著 KVO 導(dǎo)致崩潰。
? ? ? ? ? ? ? ? BayMax 系統(tǒng)還利用 Method Swizzling 實(shí)現(xiàn)了自定義的 dealloc,
? ? ? ? ? ? ? ? 在系統(tǒng) dealloc 調(diào)用之前,將多余的觀察者移除掉。
? 8.1.2:KVC
? ? ? KVC(Key Value Coding)鍵值編碼,提供一種機(jī)制來(lái)間接訪問(wèn)對(duì)象的屬性,而不是通過(guò)Setter/Getter方法進(jìn)行訪問(wèn)。
? ? ? 通常導(dǎo)致崩潰的原因不外乎鍵值設(shè)置不正確,如下:
? ? ? ? ? 1. key 不是對(duì)象的屬性
? ? ? ? ? 2. keyPath 不正確
? ? ? ? ? 3. value 為 nil,為非對(duì)象設(shè)值
? ? ? ? ? 4. key 為 nil
? ? ? 那如何防護(hù)呢,熟悉KVC機(jī)制的同學(xué)肯定清楚:runtime提供了相應(yīng)的補(bǔ)救措施來(lái)避免應(yīng)用崩潰,包括如下:
? ? ? ? ? ? setValue:forKey: 找不到相應(yīng)的key會(huì)調(diào)用 setValue: forUndefinedKey: 方法;
? ? ? ? ? ? valueForKey: 找不到相應(yīng)的keyPath會(huì)調(diào)用 valueForUndefinedKey: 方法;
? ? ? ? ? ? setValue:forKey:添加value為nil方法,會(huì)調(diào)用setNilValueForKey方法來(lái)避免;
? ? ? 因此,針對(duì)上面崩潰的前3中場(chǎng)景,就可以通過(guò)分辨實(shí)現(xiàn)上述三種方法來(lái)避免,但對(duì)于key為nil的情況該如何防護(hù)呢?
? ? ? ? ? ? 這里直接告訴答案:毅然是通過(guò)熟悉的Method Swizzle來(lái)替換原有的
? ? ? ? ? ? setValue:forKey:方法,
? ? ? ? ? ? 并判斷傳入的key是否為nil。具體代碼如下:
