OC由淺入深系列 之 KVC:(一)基本用法

一、什么是KVC

KVC(Key Value Coding)直譯為鍵值編碼,通俗的來講就是蘋果提供了一套通過字符串runtime的方式訪問屬性的方法。KVC是用過Category的方式實現(xiàn)的(見Foundation框架NSKeyValueCoding.h),這就意味著幾乎所有繼承NSObject的對象,都可以使用KVC。

二、主要方法功能說明
//直接通過Key來取值
-   (nullable id)valueForKey:(NSString *)key;

//通過Key來設(shè)值
-   (void)setValue:(nullable id)value forKey:(NSString *)key;

//通過KeyPath來取值
-   (nullable id)valueForKeyPath:(NSString *)keyPath;

//通過KeyPath來設(shè)值
-   (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

//是否允許使用KVC直接訪問實例變量, 默認(rèn)YES
-   (BOOL)accessInstanceVariablesDirectly;

//校驗值是否正確;不正確的值將被替換值或者拒絕設(shè)置新值并返回錯誤原因。
-   (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

//這是集合操作的API,里面還有一系列這樣的API,如果屬性是一個NSMutableArray,那么可以用這個方法來返回。
-   (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

//如果Key不存在,且沒有KVC無法搜索到任何和Key有關(guān)的字段或者屬性,則會調(diào)用這個方法,默認(rèn)是拋出異常。
-   (nullable id)valueForUndefinedKey:(NSString *)key;

//同上
-   (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

//如果你在SetValue方法時面給Value傳nil,則會調(diào)用這個方法
-   (void)setNilValueForKey:(NSString *)key;

//輸入一組key,返回該組key對應(yīng)的Value,再轉(zhuǎn)成字典返回,用于將Model轉(zhuǎn)到字典。
-   (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys;

三、使用方法

首先假設(shè)現(xiàn)有‘商品’類 Product,以及商品對象prod;代碼如下:

@interface Product : NSObject

@property (nonatomic, copy) NSString *name;    //商品名字
@property (nonatomic, assign) float price;     //商品價格
@property (nonatomic, strong) Factory *factory; //生產(chǎn)工廠

@end

int main(int argc, char * argv[]) {
    Product *prod = [[Product alloc] init];
    prod.name = @"商品名字";
    prod.price = 100;
}

1、取值的用法:

格式:id value = [obj valueForKey:key];
舉例:NSString *name = [prod valueForKey:@"name"];

取值時,key 的取值就是用屬性名聲明的字符串。當(dāng)我們發(fā)送valueForKey:消息時,系統(tǒng)會根據(jù)key值按照如下的步驟進行查找:

第一步:查找 -get<Key>, -<key>, or -is<Key>方法(標(biāo)量會被轉(zhuǎn)化成NSNumber,NSValue等);如果沒找到,則進行第二步

第二步::查找NSOrderSet的 -countOf , -indexInOfObject: and -objectInAtIndex: ,-AtIndexes: ,如果有,則產(chǎn)生一個NSOderSet的代理類,通過找到的方法組合響應(yīng)valueForKey方法;如果沒有則進行第三步

第三步:查找NSArray的-countOf, -objectInAtIndex: ,-AtIndexes: 如果有,則產(chǎn)上NSArray的代理類,通過找的方法組合響應(yīng)valueForKey方法 ;否則進行第四步

第四步:查找NSSet的-countOf, -enumeratorOf, and -memberOf: 如果有,則創(chuàng)建一個NSSet的代理類,通過找的方法組合響應(yīng)valueForKey方法;否則進行第五步

第五步:如果+accessInstanceVariablesDirectly返回YES,則按順序查找_<key>, _is<Key>, <key>, or is<Key>,如果還未找到,則進行第六步

第六步:觸發(fā)valueForUndefinedKey方法(默認(rèn)會拋出異常)

2、賦值的用法:
格式:[obj setValue:value forKey:key];

舉例:[prod setValue:@"新商品" forKey:@"name"];

當(dāng)我們發(fā)送setValue:forKey:消息時,系統(tǒng)會按照如下方式進行查找賦值:

第一步:查找-set:方法,如果找到則執(zhí)行該方法(value必須為對象類型,否則會觸發(fā)setNilValueForKey方法);如果沒有找到,則進行第二步

第二步:如果+accessInstanceVariablesDirectly方法返回的是YES,則按順序查找 _<key>, _is<Key>, <key>, or is<Key>方法,如果找到了就執(zhí)行賦值操作(對象類型:先釋放舊對象,再進行賦值操作;基本類型:把對象類型轉(zhuǎn)化為基本類型,如NSNumber類型轉(zhuǎn)化為int,long 等)

第三步:觸發(fā)-setValue:forUndefinedKey:方法(默認(rèn)會拋出一個異常)

賦值時,value必須是對象類型,如果不是對象類型,編譯器會報錯。如果value傳nil會怎么樣呢?我們來測試一下:

    [prod setValue:nil forKey:@"name"];       //成功賦值
    NSLog(@"name = %@",[prod valueForKey:@"name"]);

    [prod setValue:nil forKey:@"price"];      //崩潰
    NSLog(@"price = %@",[prod valueForKey:@"price"]);

    [prod setValue:nil forKey:@"size"];       //崩潰
    NSLog(@"size = %@",[prod valueForKey:@"size"]);

在以上實驗中,price 和 size 的都會拋出'NSInvalidArgumentException', reason: '[<Product 0x6000007bae80> setNilValueForKey]: could not set nil as the value for the key,如果在Product類中實現(xiàn)setNilValueForKey:方法,就不會崩潰,由此我們得出結(jié)論:

當(dāng)value值為nil時,如果屬性的數(shù)據(jù)類型為對象類型,則會把nil賦值給對應(yīng)的屬性;如果屬性的數(shù)據(jù)類型為基本數(shù)據(jù)類型時,則會觸發(fā)setNilValueForKey:方法 ,如果該方法沒重寫,則會拋出異常。

還有一點值得注意:KVC可以訪問私有成員變量,可以通過accessInstanceVariablesDirectly方法禁用對私有成員變量的訪問

3、keyPath方式使用KVC
通過知識點1和2,我們可以實現(xiàn)對屬性的賦值和取值操作,但是對于多層級的屬性的取值/賦值操作就比較麻煩了。比如:

Product類包含一個Factory(工廠)類型的屬性,描述了商品的產(chǎn)地信息,我們要獲取prod對象的生產(chǎn)地址,該怎么辦呢?

一種寫法是這樣的:通過 Factory *factory = [prod valueForKey:@"factory"]然后再調(diào)用 NSString *address = [factory valueForKey:@"address"]獲取地址。這種方法能夠?qū)崿F(xiàn)我們的功能,但是比較笨拙。KVC體統(tǒng)了一種更簡單的方式:keyPath(關(guān)鍵路徑):將要訪問屬性的層級關(guān)系一一連接起來,用.分割,即可組成關(guān)鍵路徑。例子中的關(guān)鍵路徑可以寫為:@"factory.address"。調(diào)用KVC的: -(void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;(nullable id)valueForKeyPath:(NSString *)keyPath;方法集合實現(xiàn)對屬性的賦值,取值操作。代碼如下:

 [prod setValue:@"北京市海淀區(qū)西北旺百度大廈" forKeyPath:@"factory.address"];
 NSString *address = [prod valueForKeyPath:@"factory.address"];
 NSLog(@"生產(chǎn)地址:%@",address);

四、小結(jié)

KVC提供了一套通過字符串訪問屬性,私有成員變量的方式。在類的聲明不透明的情況下,可以通過這種方式進行取值、賦值操作。對私有變量的訪問要看accessInstanceVariablesDirectly返回值是YES還是NO,YES標(biāo)識允許,NO表示不允許。keyPath在多層嵌套的屬性訪問時,更為方便。

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

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

  • KVC KVC定義 KVC(Key-value coding)鍵值編碼,就是指iOS的開發(fā)中,可以允許開發(fā)者通過K...
    暮年古稀ZC閱讀 2,289評論 2 9
  • KVC(Key-valuecoding)鍵值編碼,單看這個名字可能不太好理解。其實翻譯一下就很簡單了,就是指iOS...
    榕樹頭閱讀 769評論 0 2
  • KVC(Key-value coding)鍵值編碼,單看這個名字可能不太好理解。其實翻譯一下就很簡單了,就是指iO...
    我的夢工廠閱讀 939評論 1 8
  • 關(guān)于鍵值編碼 鍵值編碼(KVC)是一種由NSKeyValueCoding非正式協(xié)議提供的機制,對象采用該機制來提供...
    漸z閱讀 1,175評論 0 0
  • 源碼加翻譯 #import <Foundation/NSArray.h> #import <Foundation/...
    CAICAI0閱讀 1,232評論 0 50

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