標籤:
KVO與KVC詳解
由於ObjC主要基於Smalltalk進行設計,因此它有很多類似於Ruby、Python的動態特性,例如動態類型、動態載入、動態綁定等。今天我們著重介紹ObjC中的索引值編碼(KVC)、索引值監聽(KVO)特性:
- 索引值編碼KVC
- 索引值監聽KVO
索引值編碼KVC
我們知道在C#中可以通過反射讀寫一個對象的屬性,有時候這種方式特別方便,因為你可以利用字串的方式去動態控制一個對象。其實由於ObjC的語言特性,你根部不必進行任何操作就可以進行屬性的動態讀寫,這種方式就是Key Value Coding(簡稱KVC)。
KVC的操作方法由NSKeyValueCoding協議提供,而NSObject就實現了這個協議,也就是說ObjC中幾乎所有的對象都支援KVC操作,常用的KVC操作方法如下:
- 動態設定: setValue:屬性值 forKey:屬性名稱(用於簡單路徑)、setValue:屬性值 forKeyPath:屬性路徑(用於複合路徑,例如Person有一個Account類型的屬性,那麼person.account就是一個複合屬性)
- 動態讀取: valueForKey:屬性名稱 、valueForKeyPath:屬性名稱(用於複合路徑)
索引值監聽KVO
我們知道在WPF、Silverlight中都有一種雙向繫結機制,如果資料模型修改了之後會立即反映到UI視圖上,類似的還有如今比較流行的基於MVVM設計模式的前端架構,例如Knockout.js。其實在ObjC中原生就支援這種機制,它叫做Key Value Observing(簡稱KVO)。KVO其實是一種觀察者模式,利用它可以很容易實現視圖組件和資料模型的分離,當資料模型的屬性值改變之後作為監聽器的視圖組件就會被激發,激發時就會回調監聽器自身。在ObjC中要實現KVO則必須實現NSKeyValueObServing協議,不過幸運的是NSObject已經實現了該協議,因此幾乎所有的ObjC對象都可以使用KVO。
在ObjC中使用KVO操作常用的方法如下:
- 註冊指定Key路徑的監聽器: addObserver: forKeyPath: options: context:
- 刪除指定Key路徑的監聽器: removeObserver: forKeyPath、removeObserver: forKeyPath: context:
- 回調監聽: observeValueForKeyPath: ofObject: change: context:
KVO的使用步驟也比較簡單:
- 通過addObserver: forKeyPath: options: context:為被監聽對象(它通常是資料模型)註冊監聽器
- 重寫監聽器的observeValueForKeyPath: ofObject: change: context:方法
/*********************************KVC/KVO*******************************************/
KVC
kvc在實際開發中使用最多的地方一般有兩個
- 字典轉模型:但是這個有一個局限性,如果他的屬性也是一個模型的時候不能使用這個方法,或者不能一一對應的時候
- 通過屬性鍵設定對應屬性的值
字典轉模型:
1 #pragma mark 簡單字典轉模型 2 NSDictionary *dict = @{ 3 @"name" : @"jack", 4 @"money" : @"20.7", 5 }; 6 7 [p setValuesForKeysWithDictionary:dict]; 8 NSLog(@"name = %@, money = %f", p.name, p.money); 9 10 #pragma mark 複雜模型11 NSDictionary *dict = @{12 @"name" : @"jack",13 @"money" : @"20.7",14 @"dog" : @{15 @"name" : @"wangcai",16 @"price" : @"10.8"17 },18 @"books" : @[19 @{20 @"name" : @"5分鐘突破iOS開發",21 @"price" : @"19.8"22 },23 @{24 @"name" : @"3分鐘突破iOS開發",25 @"price" : @"24.8"26 },27 @{28 @"name" : @"1分鐘突破iOS開發",29 @"price" : @"29.8"30 }31 ]32 };33 [p setValuesForKeysWithDictionary:dict];34 NSLog(@"%@", p);35 }
設定屬性的值:
1 #pragma mark 單個值 2 p.name = @"lnj"; 3 p.money = 99.8; 4 5 [p setValue:@"lnj" forKey:@"name"]; 6 [p setValue:@(99.8) forKey:@"money"]; 7 NSLog(@"name = %@, money = %f", p.name, p.money); 8 9 #pragma mark 多層賦值10 11 p.dog = [XMGDog new];12 p.dog.name = @"wangcai";13 p.dog.price = 998;14 15 [p.dog setValue:@"wangcai" forKey:@"name"];16 [p.dog setValue:@(998) forKey:@"price"];17 18 [p setValue:@"wangcai" forKeyPath:@"dog.name"];19 [p setValue:@(998) forKeyPath:@"dog.price"];20 NSLog(@"name = %@, price = %f", p.dog.name, p.dog.price);21 22 #pragma mark 私人屬性23 24 [p setValue:@"lnj" forKeyPath:@"_name"];25 [p setValue:@(30) forKeyPath:@"_age"];26 27 NSLog(@"name = %@", p.name);28 [p say];
KVO
1:建立一個對象
1 iCocos *stu = [[iCocos alloc] init]; 2
2:註冊指定Key路徑的監聽器,並且修改相應的屬性
1 [stu addObserver:self forKeyPath:@"score" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];2 3 stu.score = 90;4 5 stu.score = 100;
3:刪除指定Key路徑的監聽器
1 #warning 注意只有調用set方法修改才能監聽到 2 內部實現原理是: 系統會自動給我們產生一個子類, 然後重寫子類的set方法, 在set方法中通知監聽3 stu->_score = 250;4 [stu removeObserver:self forKeyPath:@"score"];
4:回調監聽
1 - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context2 {3 NSLog(@"keyPath = %@, obj = %@, change = %@, context = %@", keyPath, object, change, context);4 }
KVO內部實現機制:
系統會自動給我們產生一個子類, 然後重寫子類的set方法, 在set方法中通知監聽
iOS開發——實用篇&KVO與KVC詳解