KVC 键值编码协议 即NSKeyValueCoding协议,是NSObject的一个非正式协议。
它的作用是我们可以通过- (nullable id)valueForKey:(NSString *)key;
和- (void)setValue:(nullable id)value forKey:(NSString *)key;
这两个方法实现对对象“属性”的读写。
我认为要理解KVC,理解上面两个方法即可。
- (nullable id)valueForKey:(NSString *)key;
根据key来获取对象的某个“属性”值
当这个方法调用的时候,操作过程如下:
- 查找当前对象所属类的方法列表,找到访问方法。
方法查找的顺序是-get<Key>
,-<key>
,-is<Key>
,如果找到了访问方法:- 如果发现返回值是一个对象指针类型,就直接返回;
- 如果是基本类型,就判断是否支持NSNumber,如果支持就直接包裹成NSNumber类型返回,如果不支持,就包裹成NSValue类型返回。
- 如果找不到访问方法,就先判断当前对象的
accessInstanceVariablesDirectly
是否为YES
。
如果是YES
,就按照_<key>
,_is<Key>
,<key>
, oris<Key>
的顺序去找对应的实例变量,找到实例变量后,获取实例变量的值,然后按照上面的逻辑进行返回。 - 如果访问方法和实例变量都找不到,
-valueForUndefinedKey:
方法将会被调用。这个方法的默认实现是抛出异常。
- (void)setValue:(nullable id)value forKey:(NSString *)key;
根据key来设置对象的某个“属性”值
操作如下:
-
查找当前对象所属类的方法列表,找到对应的
-set<Key>:
方法。- 如果setter方法的参数是对象指针类型,就直接调用。
- 如果不是对象指针类型,并且此时传过来的value是nil,就会调用
-setNilValueForKey:
,该方法的默认实现是抛出异常 - 如果传过来的参数是NSNumber类型的,在setter方法调用之前,会将参数先转换成对应的基本数据类型。
- 如果传过来的参数是NSValue类型的,在setter方法调用之前,会将参数先转换成对应的数据类型。
-
如果找不到setter方法,就先判断当前对象的
accessInstanceVariablesDirectly
是否为YES
如果是YES
,就根据_<key>
,_is<Key>
,<key>
, oris<Key>
的顺序去查找对应的实例变量。找到的话,就按照上面的逻辑进行赋值操作 -
当setter方法和实例变量都找不到的话,就会调用
-setValue:forUndefinedKey:
方法,该方法的默认实现是抛出异常。
总结:
从上面看,都是先查找对应的访问方法,找不到的情况下找实例变量,如果还找不到,就会调用默认的方法并抛出异常。
从上面的过程看,总结了以下需要注意的几点:
- 给类添加方法的时候,一定要注意避免以 get,set,is,<key>等开头。因为一旦外人使用了kvc的方式去更改东西,如果方法名字命中了就会被调用,可能造成一些意外的情况。
- 除非有必要,个人觉得最好还是使用正常的方式去修改对象的“属性”。因为相对于直接去修改,kvc做了较多的额外工作(比如查找对应方法和变量的时候相当于是判断一个数组里的某个元素是否在另一个数组里存在,NSNumber和NSValue的开箱和闭箱等)。
网友评论