KVC是一种通过字符串的名字(key)来访问修改类属性值的机制。而不是通过Setter、Getter方法直接访问。
基本使用的方法有:
- (nullableid)valueForKey:(NSString*)key;//通过Key来取值
- (void)setValue:(nullableid)value forKey:(NSString*)key;//通过Key来设值
- (nullableid)valueForKeyPath:(NSString*)keyPath;//通过KeyPath来取值(对象包含对象这种情况)
- (void)setValue:(nullableid)value forKeyPath:(NSString*)keyPath;//通过KeyPath来设值
在设置或者取值的时候默认情况下会根据:_key->_iskey->key->iskey的顺序搜索成员
除非:
+ (BOOL)accessInstanceVariablesDirectly;
这个方法重写返回NO。(是否可以直接访问成员变量)
设置值的实现:
如果这个方法返回设置成NO,设置值的时候会去看setKey:这个方法,如果没找到则直接调用
- (void)setValue:(id)value forUndefinedKey:(NSString*)key 这个方法
如果返回设置或者默认是YES,那么没有找到setKey:_setKey:或者 setIsKey: 这个方法时会继续向下搜索按照这个顺序寻找:_key->_iskey->key->iskey搜索成员。
设置值的实现检验:
1.建一个Person类, .h:
做如下测试:
打印出:=====setUserName AfterSetuserName:(null) AfterSet_userName:(null) AfterSetisUserName:(null) AfterSet_isUserName:(null)
说明: setUserName: 这个方法的优先级最高
然后注释掉 setUserName这个方法重新运行
打印出:===== _setUserName AfterSetuserName:(null) AfterSet_userName:(null) AfterSetisUserName:(null) AfterSet_isUserName:(null)
说明:这个_setUserName:这个方法的优先级第二
同理:setIsUserName:这个方法的优先级第三
把三个set方法都注释掉运行会打印出:
AfterSetuserName:(null) AfterSet_userName:testname AfterSetisUserName:(null) AfterSet_isUserName:(null)
说明访问成员变量的时候:_userName的优先级最高。
注释掉成员变量_userName。打印出:AfterSet_isUserName:testname
说明_isUserName的优先级第二,同理可得:userName第三 isUserName第四。
总结KVC的设置:
依次查找 setUserName: ->_setUserName:-> setIsUserName: 这三个方法。如果没找到。去看-(BOOL)accessInstanceVariablesDirectly这个函数返回的是YES还是NO。
返回YES则继续按顺序查找:_key->_iskey->key->iskey成员 都找不到则会调用这个函数:- (void)setValue:(id)value forUndefinedKey:(NSString*)key。
返回NO如果三个set方法没找着,则也会直接调用这个函数- (void)setValue:(id)value forUndefinedKey:(NSString*)key。
取值的实现:
按先后顺序搜索getKey:、key、isKey、_key四个方法,若某一个方法被实现,取到的即是方法返回的值,后面的方法不再运行。如果是BOOL或者Int等值类型, 会将其包装成一个NSNumber对象。
若这三个方法都没有找到,则会调用+ (BOOL)accessInstanceVariablesDirectly方法判断是否允许取成员变量的值。
若返回NO,直接调用- (nullable id)valueForUndefinedKey:(NSString *)key方法,默认是奔溃。
若返回YES,会按先后顺序取_key、_isKey、 key、isKey的值
返回YES时,_key、_isKey、 key、isKey的值都没取到,调用- (nullable id)valueForUndefinedKey:(NSString *)key方法。
验证方法与上面设置值类似。
多个值进行操作:
KVC还有更强大的功能,可以根据给定的一组key,获取到一组value,并且以字典的形式返回,获取到字典后可以通过key从字典中获取到value。
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
同样,也可以通过KVC进行批量赋值。在对象调用setValuesForKeysWithDictionary:方法时,可以传入一个包含key、value的字典进去,KVC可以将所有数据按照属性名和字典的key进行匹配,并将value给User对象的属性赋值。
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
这个功能在服务器json数据转模型很实用,需要注意的是,转换时需要服务器数据和类定义匹配,字段数量和字段名都应该匹配。如果User比服务器数据多,则服务器没传的字段为空。如果服务端传递的数据User中没有定义,则会导致崩溃。
NSArray和NSDictionary等集合对象,value都不能是nil,否则会导致Crash。
异常信息:
当根据KVC搜索规则,没有搜索到对应的key或者keyPath,则会调用对应的异常方法。异常方法的默认实现,在异常发生时会抛出一个NSUndefinedKeyException的异常,并且应用程序Crash。
我们可以重写下面两个方法,根据业务需求合理的处理KVC导致的异常。
- (nullable id)valueForUndefinedKey:(NSString *)key;
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
网友评论