- (void)setValue:(id)value forKey:(NSString *)key
方法,實(shí)現(xiàn)原理與驗(yàn)證
功能:使用一個(gè)字符串標(biāo)示符給一個(gè)對象的屬性賦值.它支持普通對象和集合對象
這個(gè)方法的默認(rèn)實(shí)現(xiàn)如下:
(1).首先去接收者(調(diào)用方法的那個(gè)對象)的類中查找與key相匹配的訪問器方法(-set<Key>
),如果找到了一個(gè)方法,就檢查它參數(shù)的類型,如果它的參數(shù)類型不是一個(gè)對象指針類型,但是只為nil
,就會執(zhí)行setNilValueForKey:
方法,setNilValueForKey:
方法的默認(rèn)實(shí)現(xiàn),是產(chǎn)生一個(gè)NSInvalidArgumentException
的異常,但是你可以重寫這個(gè)方法.如果方法參數(shù)的類是一個(gè)對象指針類型,就會簡單的執(zhí)行這個(gè)方法,傳入對應(yīng)的參數(shù).如果方法的參數(shù)類型是NSNumber
或NSValue
的對應(yīng)的基本類型,先把它轉(zhuǎn)換為基本數(shù)據(jù)類,再執(zhí)行方法,傳入轉(zhuǎn)換后的數(shù)據(jù).****
(2).如果沒有對應(yīng)的訪問器方法(setter
方法),如果接受者的類的+accessInstanceVariablesDirectly
方法返回YES
,那么就查找這個(gè)接受者的與key
相匹配的實(shí)例變量(匹配模式為_<key>
,_is<Key>
,<key>
,is<Key>
):比如:key
為age
,只要屬性存在_age
,_isAge
,age
,isAge
中的其中一個(gè)就認(rèn)為匹配上了,如果找到這樣的一個(gè)實(shí)例變量,并且的類型是一個(gè)對象指針類型,首先released
對象上的舊值,然后把傳入的新值retain
后的傳入的值賦值該成員變量,如果方法的參數(shù)類型是NSNumber
或NSValue
的對應(yīng)的基本類型,先把它轉(zhuǎn)換為基本數(shù)據(jù)類,再執(zhí)行方法,傳入轉(zhuǎn)換后的數(shù)據(jù).
(3).如果訪問器方法和實(shí)例變量都沒有找到,執(zhí)行setValue:forUndefinedKey:
方法,該方法的默認(rèn)實(shí)現(xiàn)是產(chǎn)生一個(gè) NSUndefinedKeyException
類型的異常,但是我們可以重寫setValue:forUndefinedKey:方法
驗(yàn)證:
定義一個(gè)Person類:如下
@interface Person : NSObject
{
NSString *_name;
int _age;
NSString *_address;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic,assign) int age;
@end
@implementation Person
-(void)setName:(NSString *)name
{
NSLog(@"%s----------%@",__func__,name);
_name = name;
}
- (void) setAge:(int)age
{
_age = age;
NSLog(@"%s------%d",__func__,age);
}
- (int) age
{
NSLog(@"%s------%d",__func__,_age);
return _age;
}
- (NSString *) name
{
NSLog(@"%s----------%@",__func__,_name);
return _name;
}
@end
測試代碼
1)驗(yàn)證: setValue:forKey:確實(shí)會調(diào)用-set<Key>方法
Person *p = [[Person alloc] init];
[p setValue:@"小明" forKey:@"name"];
輸出結(jié)果
2015-08-15 20:56:56.975 company[1254:98490] -[Person setName:]----------小明
2015-08-15 20:56:56.975 company[1254:98490] -[Person setAge:]------10
2)驗(yàn)證:如果它的參數(shù)類型不是一個(gè)對象指針類型,但是只為nil
,就會執(zhí)行setNilValueForKey:
方法,setNilValueForKey:
方法的默認(rèn)實(shí)現(xiàn),是產(chǎn)生一個(gè)NSInvalidArgumentException
的異常
測試代碼
[p setValue:nil forKey:@"age"];
運(yùn)行結(jié)果:
2015-08-15 20:59:36.111 company[1300:100841]
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
3)可以重寫這個(gè)方法setNilValueForKey:
在Person類的實(shí)現(xiàn)文件中,重寫setNilValueForKey:
- (void) setNilValueForKey:(NSString *)key
{
NSLog(@"%s",__func__);
}
再次運(yùn)行,結(jié)果:
2015-08-15 21:29:21.167 company[528:6226] -[Person setNilValueForKey:]
4)驗(yàn)證如果方法的參數(shù)類型是NSNumber
或NSValue
的對應(yīng)的基本類型,先把它轉(zhuǎn)換為基本數(shù)據(jù)類,再執(zhí)行方法,傳入轉(zhuǎn)換后的數(shù)據(jù),測試代碼
Person.m 文件中:
- (void) setAge:(int)age
{
_age = age;
NSLog(@"%s------%d",__func__,age);
}
測試方法中
[p setValue:@(10) forKey:@"age"];
執(zhí)行結(jié)果
2015-08-15 21:54:23.477 company[607:15602] -[Person setAge:]------10
5)驗(yàn)證如果如果沒有對應(yīng)的訪問器方法(setter
方法),如果接受者的類的+accessInstanceVariablesDirectly
方法返回YES
,那么就查找這個(gè)接受者的與key
相匹配的實(shí)例變量(匹配模式為_<key>
,_is<Key>
,<key>
,is<Key>
):比如:key
為age
,只要屬性存在_age
,_isAge
,age
,isAge
中的其中一個(gè)就認(rèn)為匹配上了,如果找到這樣的一個(gè)實(shí)例變量,并且的類型是一個(gè)對象指針類型,首先released
對象上的舊值,然后把傳入的新值retain
后的傳入的值賦值該成員變量,如果方法的參數(shù)類型是NSNumber
或NSValue
的對應(yīng)的基本類型,先把它轉(zhuǎn)換為基本數(shù)據(jù)類,再執(zhí)行方法,傳入轉(zhuǎn)換后的數(shù)據(jù).
驗(yàn)證:+accessInstanceVariablesDirectly默認(rèn)返回YES
測試代碼
NSLog(@"%d",[Person accessInstanceVariablesDirectly]);
輸出結(jié)果:
2015-08-15 22:05:22.646 company[782:21098] 1
在Person
類中分別使用
@interface Person : NSObject
{
// NSString *address;
// NSString *_address;
// 注意is后面第一個(gè)字母必須大寫否則會產(chǎn)生NSUnknownKeyException異常
// NSString *isAddress;
NSString *_isAddress;
}
測試代碼
NSLog(@"%d",[Person accessInstanceVariablesDirectly]);
[p setValue:@"金燕龍大廈" forKey:@"address"];
NSString *address = [p valueForKey:@"address"];
輸出結(jié)果:
2015-08-15 22:05:22.646 company[782:21098] 金燕龍大廈
6)驗(yàn)證:如果訪問器方法和實(shí)例變量都沒有找到,執(zhí)行setValue:forUndefinedKey:
方法,該方法的默認(rèn)實(shí)現(xiàn)是產(chǎn)生一個(gè) NSUndefinedKeyException
類型的異常,但是我們可以重寫setValue:forUndefinedKey:
方法
測試代碼:
[p setValue:@"美女" forKey:@"老婆"];
結(jié)果產(chǎn)生一個(gè)NSUnknownKeyException:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x7fd0394a4c10> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key 老婆.'
在Person.m
文件中重寫 - (void)setValue:(id)value forUndefinedKey:(NSString *)key
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
NSLog(@"%s",__func__);
NSLog(@"%@=%@",key,value);
}
再次運(yùn)行程序輸出結(jié)果:
2015-08-15 22:14:19.866 company[885:25268] -[Person setValue:forUndefinedKey:]
2015-08-15 22:14:19.866 company[885:25268] 老婆=美女