iOS_KVC_分析
本文主要转载,原文链接 http://www.cocoachina.com/ios/20180305/22441.html
也参考了其他资料
一、概念定义
KVC(Key-value Coding/键值编码) :是一个非正式协议,提供了可以通过字符串间接访问类属性、方法或成员变量的一种机制。而不是通过setter 和getter 方法。(SomeBody say,KVO就是基于KVC实现的关键技术之一)
二、基础操作
KVC主要对三种类型进行操作,基础数据类型及常量、对象类型、集合类型。
@interface BankAccount : NSObject
@property (nonatomic, strong) NSNumber *currentBalance;
@property (nonatomic, strong) Person *owner;
@property (nonatomic, strong) NSArray<transaction *> *transactions;
@end</transaction *>
在使用KVC时,直接将属性名当做key,并设置value,即可对属性进行赋值。
[myAccount setValue:@(100.0) forKey:@"currentBalance"];
三、keyPath
除了对当前对象“属性”进行赋值外,还可以对更“深层”的对象进行赋值。例如:对当前对象的address属性的 street 属性进行赋值。KVC进行多级访问时,可直接利用点语法调用即可。
[myAccount setValue:@"回龙观东大街" forKey:"adress.street"];
通过keyPath对数组进行取值的时候,并且数组的对象类型都相同,可以通过valueForKeyPath:方法指定取出数组中所有对象的某个字段。例如下面这个例子:通过valueForKeyPath:方法获取数组中所有对象的name属性值,返回放入一个数组中。
NSArray *namesArr = [array valueForKeyPath:@"name"];
四、多值操作
需要注意的是,下面两个例子虽然看到了dictionary字样,但这些并不是字典的方法。
KVC强大的地方还有,可以根据一组key获取一组 value,并以字典的形式返回,获取到字典后,就可以通过key 获取value值了。
- (NSDictionary<nsstring *, id> *)dictionaryWithValuesForKeys:(NSArray<nsstring *> *)keys;</nsstring *></nsstring *, id>
同样也可以批量赋值。在对象调用setValuesForKeysWithDictionary:方法的时候,可以传入包含key、value的字典,KVC就可以讲所有的数据按照属性名按照字典的key进行匹配,并将value赋值给对象的相应
- (void)setValuesForKeysWithDictionary:(NSDictionary<nsstring *, id> *)keyedValues;</nsstring *, id>
五、实用技巧
在开发中,最常遇到的就是将字典转模型。如果在自定义的init 方法中逐个赋值,这样每次数据发生改变的时候,还需要修改赋值语句。然而,通过KVC为我们提供的赋值API,可以对数据进行批量赋值。假设,有一下json数据,并定义User类,在外界通过setValuesForKeysWithDictionary:方法对User进行赋值。
JSON数据:
{
"username": "lxz",
"age": 25,
"id": 100
}
@interface User : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) NSInteger userId;
@end
@implementation User
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
if ([key isEqualToString:@"id"]) {
self.userId = [value integerValue];
}
}
@end
赋值时会遇到一些问题,例如服务器会返回一个id字段,但是对于客户端来说id是系统保留字段,可以重写setValue:forUndefinedKey:方法并在内部处理id参数的赋值。
转换时需要服务器数据和类定义匹配,字段数量和字段名都应该匹配。如果User比服务器数据多,则服务器没传的字段为空。如果服务端传递的数据User中没有定义,则会导致崩溃。
在KVC进行属性赋值时,内部会对基础数据类型做处理,不需要手动做NSNumber的转换。需要注意的是,NSArray和NSDictionary等集合对象,value都不能是nil,否则会导致Crash。
六、异常信息
当通过KVC给某个非对象的属性赋值为nil时,此时KVC会调用属性所属对象的setNilValueForKey:方法,并抛出NSInvalidArgumentException的异常,并使应用程序Crash。
我们可以通过重写下面方法,在发生这种异常时进行处理。例如给name赋值为nil的时候,就可以重写setNilValueForKey:方法并表示name是空的。
- (void)setNilValueForKey:(NSString *)key {
if ([key isEqualToString:@"name"]) {
[self setValue:@"" forKey:@”age”];
} else {
[super setNilValueForKey:key];
}
}
集合属性操作
根据KVO的实现原理,是在运行时生成新的子类并重写其setter方法,在其内容发生改变时发送消息。但这只是对属性直接进行赋值会触发,如果属性是容器对象,对容器对象进行add或remove操作,则不会调用KVO的方法。可以通过KVC对应的API来配合使用,使容器对象内部发生改变时也能触发KVO。
在进行容器对象操作时,先调用下面方法通过key或者keyPath获取集合对象,然后再对容器对象进行add或remove等操作时,就会触发KVO的消息通知了。
(未完待续:KVC实现原理)