iOS KVC的实现原理

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 *)dictionaryWithValuesForKeys:(NSArray *)keys;

同样,也可以通过KVC进行批量赋值。在对象调用setValuesForKeysWithDictionary:方法时,可以传入一个包含key、value的字典进去,KVC可以将所有数据按照属性名和字典的key进行匹配,并将value给User对象的属性赋值。

- (void)setValuesForKeysWithDictionary:(NSDictionary *)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;

你可能感兴趣的:(iOS KVC的实现原理)