美文网首页
iOS KVC的实现原理

iOS KVC的实现原理

作者: zhongli_007 | 来源:发表于2019-07-23 15:49 被阅读0次

    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;

    相关文章

      网友评论

          本文标题:iOS KVC的实现原理

          本文链接:https://www.haomeiwen.com/subject/olydnqtx.html