KVC概念
KVC(Key-value coding)
鍵值編碼脏里,是對NSObject
的擴展來實現(xiàn)的蚊伞,Objective-C
中有個顯式的NSKeyValueCoding
類別名--NSObject(NSKeyValueCoding)
,故而所有基于NSObject
創(chuàng)建的對象都可使用
KVC相關方法
- 常見的四種方法
- (nullable id)valueForKey:(NSString *)key; //直接通過Key來取值
- (void)setValue:(nullable id)value forKey:(NSString *)key; //通過Key來設值
- (nullable id)valueForKeyPath:(NSString *)keyPath; //通過KeyPath來取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通過KeyPath來設值
- 其他方法
+ (BOOL)accessInstanceVariablesDirectly;
//默認返回YES躏敢,表示如果沒有找到Set<Key>方法的話,會按照_key,_iskey,key但壮,iskey的順序搜索成員冀泻,設置成NO就不這樣搜索
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//KVC提供屬性值正確性驗證的API,它可以用來檢查set的值是否正確蜡饵、為不正確的值做一個替換值或者拒絕設置新值并返回錯誤原因弹渔。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//這是集合操作的API,里面還有一系列這樣的API溯祸,如果屬性是一個NSMutableArray肢专,那么可以用這個方法來返回。
- (nullable id)valueForUndefinedKey:(NSString *)key;
//如果Key不存在焦辅,且沒有KVC無法搜索到任何和Key有關的字段或者屬性博杖,則會調用這個方法,默認是拋出異常筷登。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//和上一個方法一樣剃根,但這個方法是設值。
- (void)setNilValueForKey:(NSString *)key;
//如果你在SetValue方法時面給Value傳nil前方,則會調用這個方法
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
//輸入一組key,返回該組key對應的Value狈醉,再轉成字典返回,用于將Model轉到字典惠险。
KVC設值流程
通過 KVC官方文檔 得知苗傅,當調用setValue:forKey:
設置屬性value
時,其底層的執(zhí)行流程如下:
- 首先調用setter方法班巩,按照查找順序為set<Key>:-> _set<Key> -> setIs<Key>
- (void)setName:(NSString *)name{
NSLog(@"%s - %@",__func__,name);
}
- (void)_setName:(NSString *)name{
NSLog(@"%s - %@",__func__,name);
}
- (void)setIsName:(NSString *)name{
NSLog(@"%s - %@",__func__,name);
}
如上代碼:
1. 如果任一set
方法實現(xiàn)金吗,則可完成屬性賦值
注:首字母大小寫要符合
KVC
的命名規(guī)則
2.如果set方法均沒有實現(xiàn)則走下面流程
- 查找
accessInstanceVariablesDirectly
是否返回YES
- 如果為
YES
,按順序查找名稱為_<key>
趣竣、_is<Key>
摇庙、<key>
或is<Key>
的實例變量,任一變量存在
則可完成賦值 - 如果為
NO
遥缕,則繼續(xù)下面流程
- 如果為
- 如果
set
方法或者成員變量
都不
存在卫袒,系統(tǒng)將會執(zhí)行該對象的setValue:forUndefinedKey:
方法,默認是拋出異常
KVC取值流程
按順序搜索實例单匣,查找名稱為
get<Key>夕凝、<key>、is<Key>或_<key>
的第一個訪問器方法户秤。如果找到码秉,請調用它,然后繼續(xù)進行第5步
并得出結果鸡号。否則請繼續(xù)下一步
-
如果沒有找到簡單的訪問器方法转砖,請在實例中搜索名稱與模式
countOf<Key>
和objectIn<Key>AtIndex:
(對應NSArray
類定義的原始方法)和<key>AtIndexes:
(對應NSArray方法objectsAtIndexes:
)的方法。代理對象隨后將其接收的任何NSArray
消息轉換為countOf<Key>
、objectIn<Key>AtIndex:
和<key>AtIndexes:
消息的某種組合府蔗,轉換為創(chuàng)建它的鍵值編碼合規(guī)對象晋控。如果原始對象也實現(xiàn)了名稱為get<Key>:range:
的可選方法,代理對象也會在適當?shù)臅r候使用該方法姓赤。實際上赡译,與鍵值編碼兼容對象一起工作的代理對象允許基礎屬性表現(xiàn)得像NSArray
一樣,即使它不是- 如果找到了其中的第一個和其他兩個中的至少一個不铆,請創(chuàng)建一個響應所有
NSArray
方法的集合代理對象蝌焚,并返回該對象。否則誓斥,請繼續(xù)第3步只洒。
- 如果找到了其中的第一個和其他兩個中的至少一個不铆,請創(chuàng)建一個響應所有
-
如果沒有找到上面的幾種方法,則會同時查找
countOf <Key>岖食,enumeratorOf<Key>和memberOf<Key>
這三個方法- 如果找到了所有三種方法红碑,請創(chuàng)建一個響應所有
NSSet
方法的集合代理對象,并返回該對象泡垃。否則析珊,請繼續(xù)第4步。
- 如果找到了所有三種方法红碑,請創(chuàng)建一個響應所有
如果還沒有找到蔑穴,檢查類方法InstanceVariablesDirectly是否YES忠寻,依次搜索_<key>,_is<Key>存和,<key>或is<Key>的實例變量,如果搜到奕剃,直接獲取實例變量的值,進入【第五步】
-
根據(jù)搜索到的屬性值的類型捐腿,返回不同的結果
如果是對象指針纵朋,則直接返回結果如果是NSNumber支持的標量類型,則將其存儲在NSNumber實例中并返回它
如果是是NSNumber不支持的標量類型茄袖,請轉換為NSValue對象并返回該對象
-
如果上面5步的方法均失敗操软,系統(tǒng)會執(zhí)行該對象的valueForUndefinedKey:方法,默認拋出NSUndefinedKeyException類型的異常
KVC中keyPath實現(xiàn)
@interface Infor()
@property (nonatomic,copy)NSString * height;
@end
@implementation Infor
@end
@interface People()
@property (nonatomic,copy) NSString* name;
@property (nonatomic,strong) Infor* infor;
@property (nonatomic,assign) NSInteger age;
@end
@implementation People
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
People* people1 = [People new];
Infor* infor1 = [Infor new];
infor1.height = @"180";
people1.infor = infor1;
NSString * he = people1.infor.height;
NSString * he1 = [people1 valueForKeyPath:@"infor.height"];
NSLog(@"height1:%@ height2:%@",he,he1);
[people1 setValue:@"177" forKeyPath:@"infor.height"];
he = people1.infor.height;
he1 = [people1 valueForKeyPath:@"infor.height"];
NSLog(@"height1:%@ height2:%@",he,he1);
}
//[10:08:48.535] -[ViewController viewDidLoad] [第44行] height1:180 height2:180
//[10:08:48.536] -[ViewController viewDidLoad] [第48行] height1:177 height2:177
通過代碼得知宪祥,如果是復雜的數(shù)據(jù)類型聂薪,一層一層解析比較麻煩,如果只是需要其中的一個或者幾個變量蝗羊,就可以通過key
進行直接獲取藏澳,層級關系通過.
來進行鏈接,要保證鍵值的正確性
耀找,不然就會報錯
KVC自定義實現(xiàn)
自定義完整代碼