Objective-C KVC機(jī)制深入理解

1. 基本概念

MODEL


主要是英文文檔里面經(jīng)常出現(xiàn)的一些概念,講解一下,方便英文文檔的閱讀。


iOS應(yīng)用開發(fā)是遵循MVC設(shè)計(jì)模式的,Cocoa框架用Object Modeling的規(guī)則來規(guī)范一個(gè)Model的實(shí)現(xiàn)。


ObjectModeling有如下幾個(gè)概念的規(guī)定:


Entity:表示持有數(shù)據(jù)的一個(gè)實(shí)體


Property實(shí)體中的成員,分為Attribute和:Relationship


Attribute:基本類型的成員,比如:數(shù)字、NSString。


Relationship:指向其它Entity的關(guān)系型成員,它又有to 1Relationship和to manyRelationship的區(qū)別。


AccessorMethod:getter,setter。


舉例:


如下是一個(gè)部門和員工關(guān)系的Model


部門:Department


部門名稱(NSString)


成員(NSArray)


部長(Employee)


MIC


(所有成員)


老王(一個(gè)成員)


MIB


員工:Employee


名字(NSStirng)


所屬部門(Department)


小王


MIC



使用KVC、KVO的優(yōu)勢


通過規(guī)定了一組通用的Cocoa命名法則、調(diào)用規(guī)則等,實(shí)現(xiàn)了如下功能:


2 使用一對(duì)高度規(guī)范化的訪問方法,獲取以及設(shè)置任何對(duì)象的任何屬性的值。


2 通過繼承一個(gè)特定的方法,并且指定希望監(jiān)視的對(duì)象及希望監(jiān)視的屬性名稱,就能在該對(duì)象的指定屬性的值發(fā)生改變時(shí),得到一個(gè)“通知”(盡管這不是一個(gè)真正意 義上的通知),并且得到相關(guān)屬性的值的變化(原先的值和改變后的新值)。


2 通過一個(gè)簡單的函數(shù)調(diào)用,使一個(gè)視圖對(duì)象的一個(gè)指定屬性隨時(shí)隨地都和一個(gè)控制器對(duì)象或模型對(duì)象的一個(gè)指定屬性保持同步。


2. KVC


2.1 概述


KVC是KeyValue Coding的簡稱,它是一種可以直接通過字符串的名字(key)來訪問類屬性的機(jī)制。而不是通過調(diào)用Setter、Getter方法訪問。


當(dāng)使用KVO、Core Data、CocoaBindings、AppleScript(Mac支持)時(shí),KVC是關(guān)鍵技術(shù)。


2.2 如何使用KVC


關(guān)鍵方法定義在:NSKeyValueCodingprotocol


KVC支持類對(duì)象和內(nèi)建基本數(shù)據(jù)類型。


2.2.1 獲取值


valueForKey:,傳入NSString屬性的名字。


valueForKeyPath:,傳入NSString屬性的路徑,xx.xx形式。


valueForUndefinedKey它的默認(rèn)實(shí)現(xiàn)是拋出異常,可以重寫這個(gè)函數(shù)做錯(cuò)誤處理。


2.2.2 修改值


setValue:forKey:


setValue:forKeyPath:


setValue:forUndefinedKey:


setNilValueForKey: 當(dāng)對(duì)非類對(duì)象屬性設(shè)置nil時(shí),調(diào)用,默認(rèn)拋出異常。


2.2.3 一對(duì)多關(guān)系成員的情況


mutableArrayValueForKey:有序一對(duì)多關(guān)系成員 NSArray


mutableSetValueForKey:無序一對(duì)多關(guān)系成員 NSSet


示例:


2.3 KVC的實(shí)現(xiàn)細(xì)節(jié)


搜索Setter、Getter方法


 這一部分比較重要,能讓你了解到KVC調(diào)用之后,到底是怎樣獲取和設(shè)置類成員值的。


補(bǔ)充一點(diǎn)個(gè)人理解:KVC傳入的KEY為字符串,字符串轉(zhuǎn)換為OC下的類的屬性的過程基于反射機(jī)制;


2.3.1 搜索簡單的成員


如:基本類型成員,單個(gè)對(duì)象類型成員:NSInteger,NSString*成員。


a. setValue:forKey的搜索方式:


1. 首先搜索set:方法


如果成員用@property,@synthsize處理,因?yàn)锧synthsize告訴編譯器自動(dòng)生成set:格式的setter方法,所以這種情況下會(huì)直接搜索到。


注意:這里的是指成員名,而且首字母大寫。下同。


2. 上面的setter方法沒有找到,如果類方法accessInstanceVariablesDirectly返回YES(注:這是NSKeyValueCodingCatogery中實(shí)現(xiàn)的類方法,默認(rèn)實(shí)現(xiàn)為返回YES)。


那么按_,_is,,is的順序搜索成員名。


3. 如果找到設(shè)置成員的值,如果沒有調(diào)用setValue:forUndefinedKey:。


b. valueForKey:的搜索方式:


1. 首先按get、、is的順序查找getter方法,找到直接調(diào)用。如果是bool、int等內(nèi)建值類型,會(huì)做NSNumber的轉(zhuǎn)換。


2. 上面的getter沒有找到,查找countOf、objectInAtIndex:、AtIndexes格式的方法。


如果countOf和另外兩個(gè)方法中的一個(gè)找到,那么就會(huì)返回一個(gè)可以響應(yīng)NSArray所有方法的代理集合(collection proxy object)。發(fā)送給這個(gè)代理集合(collection proxy object)的NSArray消息方法,就會(huì)以countOf、objectInAtIndex:、AtIndexes這幾個(gè)方法組合的形式調(diào)用。還有一個(gè)可選的get:range:方法。


3. 還沒查到,那么查找countOf、enumeratorOf、memberOf:格式的方法。


如果這三個(gè)方法都找到,那么就返回一個(gè)可以響應(yīng)NSSet所有方法的代理集合(collection proxy object)。發(fā)送給這個(gè)代理集合(collection proxy object)的NSSet消息方法,就會(huì)以countOf、enumeratorOf、memberOf:組合的形式調(diào)用。


4. 還是沒查到,那么如果類方法accessInstanceVariablesDirectly返回YES,那么按_,_is,,is的順序直接搜索成員名。


5. 再?zèng)]查到,調(diào)用valueForUndefinedKey:。


2.3.2 查找有序集合成員,比如NSMutableArray


mutableArrayValueForKey:搜索方式如下:


1. 搜索insertObject:inAtIndex:、removeObjectFromAtIndex:或者insert:atIndexes、removeAtIndexes:格式的方法。


如果至少一個(gè)insert方法和至少一個(gè)remove方法找到,那么同樣返回一個(gè)可以響應(yīng)NSMutableArray所有方法的代理集合。那么發(fā)送給這個(gè)代理集合的NSMutableArray消息方法,以insertObject:inAtIndex:、removeObjectFromAtIndex:、insert:atIndexes、removeAtIndexes:組合的形式調(diào)用。還有兩個(gè)可選實(shí)現(xiàn)的接口:replaceObjectInAtIndex:withObject:、replaceAtIndexes:with:。


2. 否則,搜索set:格式的方法,如果找到,那么發(fā)送給代理集合的NSMutableArray最終都會(huì)調(diào)用set:方法。


也就是說,mutableArrayValueForKey取出的代理集合修改后,用set:重新賦值回去。這樣做效率會(huì)差很多,所以推薦實(shí)現(xiàn)上面的方法。


3. 否則,那么如果類方法accessInstanceVariablesDirectly返回YES,那么按_,的順序直接搜索成員名。如果找到,那么發(fā)送的NSMutableArray消息方法直接轉(zhuǎn)交給這個(gè)成員處理。


4. 再找不到,調(diào)用setValue:forUndefinedKey:。


2.3.3 搜索無序集合成員,如:NSSet。


mutableSetValueForKey:搜索方式如下:


1. 搜索addObject:、removeObject:或者add:、remove:格式的方法,如果至少一個(gè)insert方法和至少一個(gè)remove方法找到,那么返回一個(gè)可以響應(yīng)NSMutableSet所有方法的代理集合。那么發(fā)送給這個(gè)代理集合的NSMutableSet消息方法,以addObject:、removeObject:、add:、remove:組合的形式調(diào)用。還有兩個(gè)可選實(shí)現(xiàn)的接口:intersect、set:。


2. 如果reciever是ManagedObejct,那么就不會(huì)繼續(xù)搜索了。


3. 否則,搜索set:格式的方法,如果找到,那么發(fā)送給代理集合的NSMutableSet最終都會(huì)調(diào)用set:方法。也就是說,mutableSetValueForKey取出的代理集合修改后,用set:重新賦值回去。這樣做效率會(huì)差很多,所以推薦實(shí)現(xiàn)上面的方法。


4. 否則,那么如果類方法accessInstanceVariablesDirectly返回YES,那么按_,的順序直接搜索成員名。如果找到,那么發(fā)送的NSMutableSet消息方法直接轉(zhuǎn)交給這個(gè)成員處理。


5. 再找不到,調(diào)用setValue:forUndefinedKey:。


KVC還提供了下面的功能


2.4 值的正確性核查


KVC提供屬性值確認(rèn)的API,它可以用來檢查set的值是否正確、為不正確的值做一個(gè)替換值或者拒絕設(shè)置新值并返回錯(cuò)誤原因。


實(shí)現(xiàn)核查方法


為如下格式:validate:error:


如:


[cpp] view plain copy

print?

-(BOOL)validateName:(id *)ioValue error:(NSError **)outError

{

// The name must not be nil, and must be at least two characters long.

if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 2]) {

if (outError != NULL) {

NSString *errorString = NSLocalizedStringFromTable(

@"A Person's name must be at least two characters long", @"Person",

@"validation: too short name error");

NSDictionary *userInfoDict =

[NSDictionary dictionaryWithObject:errorString

forKey:NSLocalizedDescriptionKey];

*outError = [[[NSError alloc] initWithDomain:PERSON_ERROR_DOMAIN

code:PERSON_INVALID_NAME_CODE

userInfo:userInfoDict] autorelease];

}

return NO;

}

return YES;

}


-(BOOL)validateName:(id *)ioValue error:(NSError **)outError

{

? ?// The name must not be nil, and must be at least two characters long.

? ?if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 2]) {

? ? ? ?if (outError != NULL) {

? ? ? ? ? ?NSString *errorString = NSLocalizedStringFromTable(

? ? ? ? ? ? ? ? ? ?@"A Person's name must be at least two characters long", @"Person",

? ? ? ? ? ? ? ? ? ?@"validation: too short name error");

? ? ? ? ? ?NSDictionary *userInfoDict =

? ? ? ? ? ? ? ?[NSDictionary dictionaryWithObject:errorString

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?forKey:NSLocalizedDescriptionKey];

? ? ? ? ? ?*outError = [[[NSError alloc] initWithDomain:PERSON_ERROR_DOMAIN

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?code:PERSON_INVALID_NAME_CODE

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?userInfo:userInfoDict] autorelease];

? ? ? ?}

? ? ? ?return NO;

? ?}

? ?return YES;

}

調(diào)用核查方法:


validateValue:forKey:error:,默認(rèn)實(shí)現(xiàn)會(huì)搜索 validate:error:格式的核查方法,找到則調(diào)用,未找到默認(rèn)返回YES。


注意其中的內(nèi)存管理問題。


2.5 集合操作


集合操作通過對(duì)valueForKeyPath:傳遞參數(shù)來使用,一定要用在集合(如:array)上,否則產(chǎn)生運(yùn)行時(shí)刻錯(cuò)誤。其格式如下:


Left keypath部分:需要操作對(duì)象路徑。


Collectionoperator部分:通過@符號(hào)確定使用的集合操作。


Rightkey path部分:需要進(jìn)行集合操作的屬性。


2.5.1 數(shù)據(jù)操作


@avg:平均值


@count:總數(shù)


@max:最大


@min:最小


@sum:總數(shù)


確保操作的屬性為數(shù)字類型,否則運(yùn)行時(shí)刻錯(cuò)誤。


2.5.2 對(duì)象操作


針對(duì)數(shù)組的情況


@distinctUnionOfObjects:返回指定屬性去重后的值的數(shù)組


@unionOfObjects:返回指定屬性的值的數(shù)組,不去重


屬性的值不能為空,否則產(chǎn)生異常。


2.5.3 數(shù)組操作


針對(duì)數(shù)組的數(shù)組情況


@distinctUnionOfArrays:返回指定屬性去重后的值的數(shù)組


@unionOfArrays:返回指定屬性的值的數(shù)組,不去重


@distinctUnionOfSets:同上,只是返回值為NSSet


示例代碼:


2.6 效率問題


相比直接訪問KVC的效率會(huì)稍低一點(diǎn),所以只有當(dāng)你非常需要它提供的可擴(kuò)展性時(shí)才使用它。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容