KVC(Key-value coding)鍵值編碼,指iOS的開發(fā)中,可以允許開發(fā)者通過Key名直接訪問對象的屬性,或者給對象的屬性賦值。不需要調用明確的存取方法。這樣就可以在運行時動態(tài)地訪問和修改對象的屬性。而不是在編譯時確定,這也是iOS開發(fā)中的黑魔法之一。
KVC可以不通過getter/setter方法來訪問對象的屬性。
因為一個類的成員變量如果沒有提供getter/setter的話,外界就失去了對這個變量的訪問渠道。而KVC則提供了一種訪問的方法,這個在某些場合會很有威力。例如,直接修改系統(tǒng)控件的內部屬性
KVO 是 Objective-C 對觀察者設計模式的一種實現(xiàn)。【另外一種是:通知機制(notification)】;
KVO提供一種機制,指定一個被觀察對象(例如A類),當對象某個屬性(例如A中的字符串name)發(fā)生更改時,監(jiān)聽對象會獲得通知,并作出相應處理;【且不需要給被觀察的對象添加任何額外代碼,就能使用KVO機制】
在MVC設計架構下的項目,KVO機制很適合實現(xiàn)mode模型和view視圖之間的通訊。
KVC(Key-value coding)
下面是KVC最為重要的四個方法
- (nullable id)valueForKey:(NSString *)key; //直接通過Key來取值
- (void)setValue:(nullable id)value forKey:(NSString *)key; //通過Key來設值
- (nullable id)valueForKeyPath:(NSString *)keyPath; //通過KeyPath來取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通過KeyPath來設值
+ (BOOL)accessInstanceVariablesDirectly;
//默認返回YES,表示如果沒有找到Set<Key>方法的話,會按照_key,_iskey,key,iskey的順序搜索成員,設置成NO就不這樣搜索
- (nullable id)valueForUndefinedKey:(NSString *)key;
//如果Key不存在,且沒有KVC無法搜索到任何和Key有關的字段或者屬性,則會調用這個方法,默認是拋出異常。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//和上一個方法一樣,但這個方法是設值。
- (void)setNilValueForKey:(NSString *)key;
//如果你在SetValue方法時面給Value傳nil,則會調用這個方法
修改系統(tǒng)控件的內部屬性(Runtime+KVC)
比如修改tabbar底部控制欄的tabbar為自定義tabbar類型,就可以通過kvc來實現(xiàn)。
有時候一些屬性是私有變量,可以通過runtime來查找屬性值
/**
* 運行時(runtime),獲取所有成員變量
*/
- (void)getMemberVariables
{
unsigned ivarCount;
Ivar *ivars = class_copyIvarList([UIPageControl class], &ivarCount);
for (int i = 0; i < ivarCount; i ++) {
NSString *varibale = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
NSLog(@"%@",varibale);
}
}
通過KVC來修改
UIPageControl *pageControl = [[UIPageControl alloc ] init];
[pageControl setValue:[UIImage imageNamed:%@"nomal.png"] forKey:@"_pageImage"];
[pageControl setValue:[UIImage imageNamed:%@"selected.png"] forKey:@"_currentPageImage"]
KVC底層原理分析
setValue:forKey:賦值原理如下
- 去模型中查找有沒有對應的setter方法:例如:setIcon方法,有就直接調用這個setter方法給模型這個屬性賦值[self setIcon:dic[@"icon"]];
- 如果找不到setter方法,接著就會去尋找有沒有icon屬性,如果有,就直接訪問模型中的icon屬性,進行賦值,icon=dict[@"icon"];
- 如果找不到icon屬性,接著又會去尋找_icon屬性,如果有,直接進行賦值_icon=dict[@"icon"];
- 如果都找不到就會報錯:[<Flag 0X7fb74bc7a2c0> setValue:forUndefinedKey:]
- 如果對某個類,不允許使用KVC,可以通過設置 accessInstanceVariablesDirectly 控制。
KVC內部的實現(xiàn)
比如說如下的一行KVC的代碼:
[site setValue:@"sitename" forKey:@"name"];
就會被編譯器處理成:
SEL sel = sel_get_uid ("setValue:forKey:");
IMP method = objc_msg_lookup (site->isa,sel);
method(site, sel, @"sitename", @"name");
這下KVC內部的實現(xiàn)就很清楚的清楚了:一個對象在調用setValue的時候,
- (1)首先根據(jù)方法名找到運行方法的時候所需要的環(huán)境參數(shù)。
- (2)他會從自己isa指針結合環(huán)境參數(shù),找到具體的方法實現(xiàn)的接口。
- (3)再直接查找得來的具體的方法實現(xiàn)。
/
/
/
/
/
KVO
KVO 是 Objective-C 對觀察者設計模式的一種實現(xiàn)。【另外一種是:通知機制(notification)】;
KVO提供一種機制,指定一個被觀察對象(例如A類),當對象某個屬性(例如A中的字符串name)發(fā)生更改時,監(jiān)聽對象會獲得通知,并作出相應處理;【且不需要給被觀察的對象添加任何額外代碼,就能使用KVO機制】
在MVC設計架構下的項目,KVO機制很適合實現(xiàn)mode模型和view視圖之間的通訊。
notification比KVO多了發(fā)送通知的一步。
兩者都是一對多,但是對象之間直接的交互,notification明顯得多,需要notificationCenter來做為中間交互。而KVO如我們介紹的,設置觀察者->處理屬性變化,至于中間通知這一環(huán),則隱秘多了,只留一句“交由系統(tǒng)通知”,具體的可參照以上實現(xiàn)過程的剖析。
notification的優(yōu)點是監(jiān)聽不局限于屬性的變化,還可以對多種多樣的狀態(tài)變化進行監(jiān)聽,監(jiān)聽范圍廣,例如鍵盤、前后臺等系統(tǒng)通知的使用也更顯靈活方便。
與delegate的不同
和delegate一樣,KVO和NSNotification的作用都是類與類之間的通信。但是與delegate不同的是:
這兩個都是負責發(fā)送接收通知,剩下的事情由系統(tǒng)處理,所以不用返回值;而delegate 則需要通信的對象通過變量(代理)聯(lián)系;
delegate一般是一對一,而這兩個可以一對多。
KVO應用過程
1:注冊觀察者,實施監(jiān)聽;
[self.model addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
- observer 指觀察者
- keyPath 表示被觀察者的屬性
- options 決定了提供給觀察者change字典中的具體信息有哪些。 【見options解析】
- context 這個參數(shù)可以是一個 C指針,也可以是一個 對象引用,它可以作為這個context的唯一標識,也可以提供一些數(shù)據(jù)給觀察者。因為你傳進去是啥,回調時候還是回傳的還是啥
注冊監(jiān)聽,options入?yún)⑹莻€枚舉,該入?yún)⒏O(jiān)聽回調中的change呼應。。并且,以上options入?yún)r候是可以用 | 或運算進行多選的。
例如:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld。。那在change字典中就會包含屬性變化前后的值。
2:監(jiān)聽回調
-observeValueForKeyPath:ofObject:change:context:就這一個方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
- keyPath:你所觀察對象的屬性
- object:你所觀察的對象
- change:你所觀察對象屬性值的變化
}
注意:通過多options監(jiān)聽屬性的時候,例如上,并不是回到一次老值,再回調一次新值,,而是新老值都是在change字典中的。。
3:移除觀察者
你可以通過 removeObserver:forKeyPath: 或 removeObserver:forKeyPath:context: 方法來移除一個觀察。
注意:如果你的 context 是一個 對象,你必須在移除觀察之前持有它的強引用。當移除了觀察后,觀察者對象再也不會受到這個 keyPath 的通知。
KVO的實現(xiàn)原理
當觀察某對象 A 時,KVO 機制動態(tài)創(chuàng)建一個對象A當前類的子類,并為這個新的子類重寫了被觀察屬性 keyPath 的 setter 方法。setter 方法隨后負責通知觀察對象屬性的改變狀況。
參考:
KVC
KVC的理解、應用及其底層原理 http://www.lxweimin.com/p/9839ecd75377
iOS開發(fā)技巧系列---詳解KVC(我告訴你KVC的一切)http://www.lxweimin.com/p/9839ecd75377
KVO