概述:
KVC簡稱KeyValueCoding,是一個基于NSKeyValueCoding非正式協(xié)議的機(jī)制,可以允許開發(fā)者通過Key名直接訪問對象的屬性,或者給對象的屬性賦值。而不需要調(diào)用明確的存取方法。這樣就可以在運(yùn)行時(shí)動態(tài)訪問和修改對象的屬性。而不是在編譯時(shí)確定,這也是iOS開發(fā)中的黑魔法之一。很多高級的iOS開發(fā)技巧都是基于KVC實(shí)現(xiàn)的。
注:NSObject是定義了KVC的,所以繼承NSObject的對象都支持KVC,基本上所有的OC對象都支持KVC。
賦值過程:
MyObject *obj = [[MyObject alloc] init];
[obj setValue:@"哈哈哈" forKey:@"name"];
先查找相關(guān)的賦值方法,查找順序是:
????????set<Key>
? ? ? ? _set<Key>
? ? ? ?setIs<Key>
如果沒有找到相關(guān)賦值方法則調(diào)用+(BOOL)accessInstanceVariablesDirectly方法, 判斷是否可以直接訪問成員變量。(系統(tǒng)默認(rèn)是返回YES的,但可以重寫)
返回NO:調(diào)用- (void)setValue:(id)value forUndefinedKey:(NSString *)key方法并崩潰。錯誤異常是NSUnknownKeyException。
如果返回YES: 繼續(xù)找相關(guān)變量,查找順序是:
????????_<key>
????????_is<Key>
????????key
????????is<Key>
如果賦值方法和成員變量都不存在,則調(diào)用- (void)setValue:(id)value forUndefinedKey:(NSString *)key?并拋出異常NSUnknownKeyException。
取值過程:
MyObject *obj = [[MyObject alloc] init];
NSLog(@"%@",[obj valueForKey:@"name”]);
先查找相關(guān)賦值方法,查找順序是:
????????get<Key>
????????key
如果沒有找到相關(guān)方法,則調(diào)用+ (BOOL)accessInstanceVariablesDirectly?判斷是否可以直接訪問成員變量。
如果返回NO,則調(diào)用valueForUndefinedKey:, 并拋出系統(tǒng)異常:NSUnknownKeyException。
如果返回YES,則繼續(xù)查找相關(guān)變量,查找順序是
????????_<key>
????????_is<Key>
????????<key>
? ? ? ? is<Key>
如果取值方法或者成員變量都不存在,則調(diào)用valueForUndefinedKey:方法,并拋出異常:NSUnknownKeyException。
注:如果想禁用KVC,重寫+ (BOOL)accessInstanceVariablesDirectly 方法讓其返回NO即可,這樣 如果KVC找不到set<Key>屬性名時(shí),則會直接調(diào)用- (void)setValue:(id)value forUndefinedKey:(NSString *)key?并拋出異常NSUnknownKeyException。
常用場景:
1、屬性賦值
Boy *jack = [[Boy alloc] init];
jack.book = [[Book alloc] init];
[jack.book setValue:@"iOS"forKeyPath:@"name"];//方式一
[jack setValue:@"C++"forKeyPath:@"book.name"];//方式二
2、添加私有成員變量
[jack setValue:@"182" forKeyPath:@"_height"];
3、KVC可以修改類的私有變量
可以看出利用KVC可以修改類的私有變量,可以修改IOS隱藏一些屬性,如UITextField的placeHolderText默認(rèn)style在需求中達(dá)不到要求,我們可以直接通過KVC快速定義自己的style,代碼如下:
[textField setValue:[UIColor redColor] forKeyPath:@"placeholderLabel.textColor"];
4、KVC可以用來給model賦值
NSDictionary*dic = @{@"name":@"book",@"num":@"66"};? ??
GoodModel *model = [[GoodModel alloc] init];
[model setValuesForKeysWithDictionary:dic];
NSLog(@"model.name : %@",model.name);
NSLog(@"model.num : %@",model.num);
注意:如果 model 屬性和 dic 不匹配會怎樣?
第一種情況,model多一個屬性:這樣程序沒問題,model多出的屬性會是nil
第二種情況,model少一個屬性:程序會崩潰
第三種情況,model的屬性名字和dic的key不匹配 : 程序會崩潰
解決方法:?第二種和第三種崩潰的解決辦法是重寫方法 ?-(void)setValue:(id)value forUndefinedKey:(NSString *)key
- (void)setValue:(id)value forUndefinedKey:(NSString*)key{
? ? ? ? ? ? ? ?if([key isEqualToString:@"id"]){
? ? ? ? ? ? ? ? ? ? ? ? ? self.goodId = (NSString*)value;
? ? ? ? ? ? ? ? ? }
? ? }