KVC和KVO的區(qū)別

KVC和KVO的區(qū)別

是在運(yùn)行時(shí)動(dòng)態(tài)的訪問和修改對(duì)象的屬性,它的定義是對(duì)NSObjcet的拓展實(shí)現(xiàn) KVO的依賴于OC的Runtime來實(shí)現(xiàn)的,當(dāng)觀察者對(duì)象時(shí),KVO機(jī)制動(dòng)態(tài)創(chuàng)建一個(gè)對(duì)象A的子類(NSKVONotifying_A類),并為這個(gè)子類重寫了被觀察的屬性的setter方法,setter方**負(fù)責(zé)調(diào)用原來的setter方法前后,通知觀察對(duì)象發(fā)生了變化

什么是KVC和KVO?

KVC(Key-Value-Coding)內(nèi)部的實(shí)現(xiàn):一個(gè)對(duì)象在調(diào)用setValue的時(shí)候,(1)首先根據(jù)方法名找到運(yùn)行方法的時(shí)候所需要的環(huán)境參數(shù)。(2)他會(huì)從自己isa指針結(jié)合環(huán)境參數(shù),找到具體的方法實(shí)現(xiàn)的接口。

(3)再直接查找得來的具體的方法實(shí)現(xiàn)。

KVO(Key-Value-Observing):當(dāng)觀察者為一個(gè)對(duì)象的屬性進(jìn)行了注冊(cè),被觀察對(duì)象的isa指針被修改的時(shí)候,isa指針就會(huì)指向一個(gè)中間類,而不是真實(shí)的類。所以isa指針其實(shí)不需要指向?qū)嵗龑?duì)象真實(shí)的類。所以我們的程序**不要依賴于isa指針。在調(diào)用類的方法的時(shí)候,**要明確對(duì)象實(shí)例的類名。

什么是KVC

在iOS開發(fā)過程中,我們經(jīng)常會(huì)聽到或者用到KVO/KVC,但是對(duì)于什么是KVO和KVC,我們可能沒有那么了解。下面先讓我們來了解一下什么是KVC. 什么是KVC 在蘋果的**文檔中是這樣描述KVC的:它是一種通過字符串描述符而不是通過調(diào)用訪問方法或者直接使用實(shí)例變量的非直接的訪問對(duì)象屬性的機(jī)制,說白了就是KVO是一種通過非常規(guī)方法訪問成員變量或者屬性的機(jī)制,這種非常規(guī)方式就是通過一個(gè)字符串標(biāo)示符也就是所謂的key來訪問屬性或者成員變量。

而這個(gè)key一般就是屬性名或者實(shí)例變量名。

對(duì)于KVC的基本的方法都定義在NSKeyValueCoding的非正式協(xié)議中,并且NSObject默認(rèn)實(shí)現(xiàn)了該協(xié)議。 KVC不僅支持對(duì)象類型,也支持?jǐn)?shù)值類型和結(jié)構(gòu)體。非對(duì)象類型的參數(shù)和返回類型會(huì)自動(dòng)封裝成NSValue或NSNumber類型。 KVO可以用來訪問三種不同的對(duì)象值類型:屬性、一對(duì)一關(guān)系、一對(duì)多關(guān)系 屬性可以是諸如數(shù)值、字符串、bool類型等簡(jiǎn)單的值。

也可以NSNumber或者NSColor這樣的對(duì)象值。 在一對(duì)一關(guān)系里的對(duì)象可以擁有它自己的屬性,這些屬性可以在不改變對(duì)象的情況下被改變。像UIView的superView的屬性,我們可以更改superView的屬性,而不需要更改UIView。

一對(duì)多屬性是一些相關(guān)對(duì)象的**。通常用NSArray或者NSSet來存儲(chǔ)這些**。KVO也允許用戶自定義**類,但依然是像訪問NSArray或者NSSet一樣訪問它們。

鍵和鍵路徑 鍵是用來標(biāo)識(shí)一個(gè)對(duì)象屬性的字符串。一般情況下,鍵就是訪問方法或者是對(duì)象的實(shí)例變量的名字。鍵必須是ASCII編碼,以小寫字母開頭,并且不能包含空格。

舉幾個(gè)鍵的例子:age、firstName、lastNmame等。 鍵路徑是一串由點(diǎn)分隔的鍵組成的字符串,它是用來遍歷一系列的對(duì)象屬性的。**個(gè)鍵的屬性是跟接收者相關(guān)的,而每一個(gè)子系列是跟前一個(gè)屬性相關(guān)的。比如鍵路徑address.street,這個(gè)鍵路徑會(huì)首先從接收者獲得address屬性,然后從address屬性中獲得street屬性。

用KVC獲得屬性的值 方法valueForKey:會(huì)返回跟接收者相關(guān)的key的值。如果對(duì)于指定的key沒有訪問器或者實(shí)例變量,則給自己發(fā)送一個(gè)valueForUndefinedKey:消息,這個(gè)方法的默認(rèn)實(shí)現(xiàn)是拋出一個(gè)NSUndefinedKeyException。子類可以重寫這個(gè)方法。 同樣的,valueForKeyPath:返回跟接收者相關(guān)的鍵路徑的值。

對(duì)于子系列中任何不遵循KVC的對(duì)象,都會(huì)收到一個(gè)valueForUndefineKey:消息。 dictionaryWithValuesForKeys:會(huì)檢索數(shù)組中所有跟接收者相關(guān)的key的值。返回的NSDictionary中包含了數(shù)組中所有key的值。 注意:**對(duì)象,比如NSArray、NSSet和NSDictionary中不能將nil作為一個(gè)值。

相反的,應(yīng)該用NSNull對(duì)象代替nil。NSNull是一個(gè)代表nil的對(duì)象屬性。dictionaryWithValuesForKeys:和setValuesForKeysWithDictionary:方法的實(shí)現(xiàn)中,默認(rèn)會(huì)在nil和NSNull之間進(jìn)行轉(zhuǎn)換。

在你的對(duì)象中,不需要對(duì)nil做顯示的測(cè)試。 用KVC設(shè)置屬性的值 方法setValue:forKey:是將接收者中相關(guān)key的值設(shè)置成指定的值。在這個(gè)方法的實(shí)現(xiàn)中,會(huì)將NSValue的值轉(zhuǎn)換成普通的數(shù)值然后賦給屬性。

如果指定的key不存在,會(huì)給接收者發(fā)送一個(gè)setValue:forUndefinedKey:消息。這個(gè)方法的默認(rèn)實(shí)現(xiàn)是拋出一個(gè)NSUndefinedKeyException異常,子類可以重寫這個(gè)方法來自定義默認(rèn)行為。 方法setValue百科:forKeyPath:的實(shí)現(xiàn)跟前面的一樣,只不過它是用來處理鍵路徑的。 setValuesForKeysWithDictionary:方法是用指定字典里的值來賦給接收者相關(guān)的屬性。

這個(gè)方法的默認(rèn)實(shí)現(xiàn)是對(duì)每一對(duì)鍵-值都調(diào)用一次setValue:forKey:方法,并且自動(dòng)將nil轉(zhuǎn)成NSNull。 **,你要關(guān)心的當(dāng)嘗試將一個(gè)nil值賦給一個(gè)非對(duì)象類型的時(shí)候該怎么辦。這種情況下,接收者會(huì)發(fā)出一個(gè)setNilValueForKey:消息,這個(gè)方法的默認(rèn)實(shí)現(xiàn)是拋出一個(gè)NSInvalidArgumentException。

kvc和kvo原理

KVC和KVO想必都熟知的一個(gè)名詞,采用觀察者模式,那么KVC到底是個(gè)什么,KVO又是什么,它們之間是怎樣關(guān)聯(lián)的,一起來解決這些疑惑。像我們銀行卡bankCard的余額變動(dòng),會(huì)及時(shí)通知給用戶,這種場(chǎng)景就運(yùn)用了觀察者模式,達(dá)到了系統(tǒng)的高效高性能處理;當(dāng)你了解KVC機(jī)制,會(huì)恍然大悟,只要知道一個(gè)UI的結(jié)構(gòu),就能對(duì)他做任意的修改,所以了解KVC和KVO的機(jī)制原理還是很重要的。

一、首先先講KVC,KVC簡(jiǎn)稱KeyValueCoding,是一個(gè)基于NSKeyValueCoding非正式協(xié)議的機(jī)制,就是直接通過key值對(duì)對(duì)象的屬性進(jìn)行存取操作,而不是通過明確的存取方法,簡(jiǎn)而言之也就是一系列規(guī)則和方法進(jìn)行的存取操作,那接下來深入了解KVC在底層做了些什么。

注:NSObject是定義了KVC的,所以繼承NSObject的對(duì)象都支持KVC,基本上所有的OC對(duì)象都支持KVC。1、 首先我們了解取值功能,MyObject對(duì)象代碼如下: @interface MyObject : NSObject @property (nonatomic, strong)NSString *name; @end123123我們對(duì)該對(duì)象的name進(jìn)行取值myObject = [[MyObject alloc] init];myObject.name = @\”八點(diǎn)鐘\”;NSLog(@\”%@\”, myObject.name);123123當(dāng)執(zhí)行myObject.name時(shí),系統(tǒng)自動(dòng)會(huì)執(zhí)行- (NSString*)name方法,這個(gè)方法在property建立的時(shí)候,系統(tǒng)會(huì)默認(rèn)生成,也會(huì)對(duì)應(yīng)的生成setName方法。2、我們換以一種形式來訪問,在沒有主動(dòng)建立property的情況下,MyObject對(duì)象代碼如下:@interface MyObject : NSObject@end1212我們對(duì)該對(duì)象的name進(jìn)行取值myObject = [[MyObject alloc] init];NSLog(@\”%@\”,[myObject valueForKey:@\”name”]);1212當(dāng)執(zhí)行[myObject valueForKey:@”name”]時(shí),我們沒有定義屬性name,系統(tǒng)就會(huì)根據(jù)key來進(jìn)行一個(gè)搜索,那么KVC執(zhí)行規(guī)則是:依次搜索-(NSString*)getName方法,- (NSString*)name方法,- (NSString*)isName方法,這三個(gè)方法的先后順序來查找,如果有,則執(zhí)行相應(yīng)的方法,(這是按NSString類型的查找規(guī)則)。如果沒有則繼續(xù)找,因?yàn)閗ey:@”name”,系統(tǒng)也不知道查找的name是什么類型的,那么系統(tǒng)會(huì)按照其他數(shù)據(jù)類型的方式繼續(xù)找,如數(shù)組NSArray查找方式是會(huì)找這兩個(gè)方法-(NSUInteger)countOfName-(id)objectInNameAtIndex:(NSUInteger)index1212如果找到了就執(zhí)行。

依此類推還有其他數(shù)據(jù)類型的查找方式,不一一例舉出來了,重點(diǎn)是了解有這么一個(gè)過程,KVC原理。如果按照KVC規(guī)則沒有查找到相應(yīng)的方法則會(huì)調(diào)用valueForUndefinedKey:方法,系統(tǒng)直接拋出異常,程序Crash, 我們可以重寫valueForUndefinedKey這個(gè)方法,則不會(huì)導(dǎo)致系統(tǒng)拋出異常,防止程序奔潰。那么KVC還提供了valueForKeyPath:等這些取值方法。

注意如果是BOOL或者int等值類型,會(huì)做NSNumber轉(zhuǎn)換。講了這么多,來一波代碼,看現(xiàn)象:(依次注釋MyObject類里面的方法來看效果就一目了然,代碼簡(jiǎn)單,磨起鍵盤吧,高手是敲出來的,背后都有一部擼的辛酸史,實(shí)踐出真知,代碼擼起來)??二、接下來我們來研究存的操作(修改對(duì)象屬性)property的上面已經(jīng)提到了,來看另一種方式setValue:forKey或setValue:forKeyPath等方法。對(duì)象代碼:(MyObjectTwo類沒有定義任何屬性)@interface MyObjectTwo :NSObject@end1212執(zhí)行代碼myObjectTwo = [[MyObjectTwoalloc]init];[myObjectTwo setValue:@\”八點(diǎn)種學(xué)院\” forKey:@\”name\”];1212調(diào)用setValue:forkey:代碼時(shí),系統(tǒng)沒有自定屬性name,那么底層會(huì)怎么做呢, 系統(tǒng)執(zhí)行的操作:系統(tǒng)會(huì)去找setName:屬性值方法,如果沒有找到setName:方法,KVC機(jī)制會(huì)檢查(BOOL)accessInstanceVariablesDirectly方法有沒有返回YES,系統(tǒng)默認(rèn)是返回YES的,但我們可以重寫該方法,返回NO,如果是返回NO,沒找到setName方法,那么系統(tǒng)就會(huì)執(zhí)行setValue:ForUndefineKey:方法,系統(tǒng)拋出異常,程序Crash。

返回YES,繼續(xù)搜索類里面成員變量,搜索順序依次是_name、_isName、name、isName,如果找了相應(yīng)變量,就停止尋找,對(duì)該變量進(jìn)行修改。動(dòng)起手來,看機(jī)制,一切盡在擼??注意:當(dāng)對(duì)NSDictionary對(duì)象使用KVC時(shí),valueForKey:和objectForKey:效果一樣??梢钥闯隼肒VC可以修改類的私有變量,可以修改IOS隱藏一些屬性,如UITextField的placeHolderText默認(rèn)style在需求中達(dá)不到要求,我們可以直接通過KVC快速定義自己的style,代碼如下:[textField setValue:[UIColor redColor] forKeyPath:@\”placeholderLabel.textColor\”];11如果你了解一個(gè)UI的內(nèi)部結(jié)構(gòu),你可以通過KVC可以改變系統(tǒng)默認(rèn)的任何效果,KVC是不是很強(qiáng)大。

本文簡(jiǎn)單的講述了KVC的內(nèi)部原理,如果一個(gè)框架沒有KVC機(jī)制的時(shí)候,我們就可以簡(jiǎn)單實(shí)現(xiàn)自己的KVC,一個(gè)程序猿追求的應(yīng)該不是對(duì)工具的使用,而是理解原理機(jī)制,創(chuàng)造工具,那么你就是牛B的。

解釋 KVC 和 KVO

Key value coding, Key value observer. Kvc 是路徑訪問的規(guī)范,kvo 是觀察某個(gè)變量的變化過程 KVO 可以觀察某個(gè)對(duì)象的變量變化過程,KVC 是滿足被觀察的編碼規(guī)范。 KVC/KVO 類似于**,通知中心。

都是一種通訊方法。

iOS-KVC和KVO

重新整理一波KVO和KVC. 整理以前的認(rèn)識(shí), 在一個(gè)類里面創(chuàng)建屬性, 系統(tǒng)會(huì)給當(dāng)前的類創(chuàng)建帶下劃線的成員變量. 所以只要知道了屬性名稱,就可以通過KVC對(duì)類的屬性進(jìn)行賦值 問題 : 1.能不能為類的屬性通過KVC的方式賦值. 2.能不能為類的內(nèi)部成員屬性(implement里面的帶下劃線的匿名成員函數(shù))賦值 打印結(jié)果 KVO的實(shí)現(xiàn)原理