KVO
、KVC
的實現原理與應用
各種名詞:
KVO
: key value observing
KVC
: key value Coding
KVO
是什麼
KVO
是OC
對觀察者設計模式
的一種實現方式,是以非正式協議(Category)的形式定義在NSObject
中。
當一個被觀察的對象(如類A
)的某個屬性放生改變時,對象會獲得通知,並作出了相應的處理。
在MVC
設計架構下,KVO
是一種理想的Model
、View
之間的通訊機制。例如,在控制器中創建ModelA
的觀察者,當ModelA
的某個屬性發生變化時,可以通過KVO
使得控制器收到通知,並作出變化。
KVO
的實現原理
舉個栗子,當觀察對象類ClassA
的propertyX
時,KVO
機制會動態的創建ClassA
的子類,並且爲這個新的子類重現被觀察的屬性propertyX
的keyPath
的setter
方法。當propertyX
發生改變時,會調用setter
方法,這時候,setter
做了一件事情,那就是通知觀察者該屬性改變的狀態。
那麼,iOS
是如何動態創建ClassA
的呢?
Apple
使用了isa-swizzling
來實現KVO
的動態創建。當觀察ClassA
時,KVO
會動態的創建一個新的 名爲NSKVONotifying_ClassA
類,並重寫了setter
方法,該方法會負責在調用原setter
方法之前和之後通知所有觀察對象屬性值的更改情況。
NSNotifying_ClassA
類:在這個過程中,被觀察的isa
指針(即Runtime
中指向對象的類的指針)從指向的是原來的ClassA
類,被KVO
機制修改爲指向NSNotifying_ClassA
,用來實現當前類的屬性值改變監聽。
當我們手動的創建了NSNotifying_ClassA
類,那麼就會使得系統運行到註冊KVO
的那段代碼時崩潰。- 子類的setter方法剖析:查看
NSObject
的兩個方法willChangeValueForKey:
、didChangevlueForKey:
,在存取數值的前後分別會調用這兩個方法,分別在屬性改變前和改變後進行調用。
KVO
爲子類的觀察者屬性重寫調用存取方法的工作原理在代碼中相當於:
-(void)setName:(NSString *)newName{
[self willChangeValueForKey:@"name"]; //KVO在調用存取方法之前總調用
[super setValue:newName forKey:@"name"]; //調用父類的存取方法
[self didChangeValueForKey:@"name"]; //KVO在調用存取方法之後總調用
}
觀察者觀察的是屬性,只有遵循了KVO
變更屬性值的方式纔會執行KVO
的回調方法,例如是否執行了setter
方法、或者是否使用了KVC
賦值。
如果賦值的方式沒有通過setter
方法,那麼就只是改變值,不會發出通知。例如,以下方式不會觸發KVO
機制。
_name = @"newName";
KVO
實現步驟
- 註冊觀察者,實施監聽
//第一個參數observer:觀察者 (這裏觀察self.myKVO對象的屬性變化)
//第二個參數keyPath: 被觀察的屬性名稱(這裏觀察self.myKVO中num屬性值的改變)
//第三個參數options: 觀察屬性的新值、舊值等的一些配置(枚舉值,可以根據需要設置,例如這裏可以使用兩項)
//第四個參數context: 上下文,可以爲kvo的回調方法傳值(例如設定爲一個放置數據的字典)
[self.myKVO addObserver:self forKeyPath:@"num" options:
NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
- 在回調方法中處理屬性發生的變化
當屬性(keyPath)的值發生變化時,收到通知,調用以下方法:
// keyPath:屬性名稱
// object:被觀察的對象
// change:變化前後的值都存儲在change字典中
// context:註冊觀察者時,context傳過來的值
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
}
- 移除觀察者
KVC
與KVO
的不同
KVC
是一個非正式的Protocol
,使用字符串訪問一個對象實例變量的機制。而不是通過setter
、getter
方法等顯式的存取方式去訪問。例如,在動畫中,可以使用KVC
的方式改變view的參數。
KVO
是一種機制,當制定的對象屬性被修改後,對象就會接受到通知。
KVO
與notification
的不同
兩者都是一對多,但是對象之間直接的交互,notification
明顯得多,需要一箇中間交互NSNotificationCenter
。