定义
全称 Key-Value-Coding
,又叫键值编码。
常用 API
- (nullable id)valueForKey:(NSString *)key; //直接通过Key来取值
- (void)setValue:(nullable id)value forKey:(NSString *)key; //通过Key来设值
- (nullable id)valueForKeyPath:(NSString *)keyPath; //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通过KeyPath来设值
+ (BOOL)accessInstanceVariablesDirectly;
//默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//KVC提供属性值正确性验证的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回。
- (nullable id)valueForUndefinedKey:(NSString *)key;
//如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//和上一个方法一样,但这个方法是设值。
- (void)setNilValueForKey:(NSString *)key;
//如果你在SetValue方法时面给Value传nil,则会调用这个方法
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
//输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。
原理
赋值原理
- (void)setValue:(nullable id)value forKey:(NSString *)key;
- 调用
setValue:forKey:
, - 触发
setKey:
,- 如果
setKey:
不存在,则会寻找_setKey:
, - 如果这两个方法都没有找到,会调用
accessInstanceVariablesDirectly
,- 如果返回
NO
, 意味着不允许访问成员变量,然后会调用setValue:forUndefineKey
,并抛出异常。 - 如果返回
YES
,会去根据_key
、_isKey
、key
、isKey
的顺序去修改,如果找到,则对成员变量进行赋值,如果没有找到,会调用setValue:forUndefineKey
,并抛出异常
- 如果返回
- 如果
image.png
取值原理
- (nullable id)valueForKey:(NSString *)key;
调用
valueForKey:
-
寻找
getKey:
,如果找到则完成调用。- 否则寻找
key
,如果找到则完成调用。- 否则寻找
isKey
, 如果找到则完成调用。- 否则寻找
_key
, 如果找到则完成调用。
- 否则寻找
- 否则寻找
- 否则寻找
-
如果上面四个方法都没有找到,则调用
accessInstanceVariablesDirectly:
来判断是否允许寻找成员变量。-
如果返回
YES
, 则寻找对应的成员变量。- 首先寻找
_key
, 如果找到则返回。- 否则寻找
_isKey
,如果找到则返回。- 否则寻找
key
,如果找到则返回。- 否则寻找
isKey
,如果找到则返回。- 如果都没找到,则调用
valueForUndefineKey:
, 并抛出异常。
- 如果都没找到,则调用
- 否则寻找
- 否则寻找
- 否则寻找
- 首先寻找
如果返回
NO
, 则直接调用valueForUndefineKey:
, 并抛出异常。
-
image.png
空值异常
通常情况下, setValue: forKey:
在非对象属性(也就是基本类型)的时候,需要我们传递一个不是 nil
的值,当传递一个 nil
值时,会触发 setNilValueForKey :
,并引发崩溃,因此,我们需要重写这个方法。
异常:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[<Person 0x100629230> setNilValueForKey]: could not set nil as the value for the key age.'
- (void)setNilValueForKey:(NSString *)key{
NSLog(@"key 的值为空了- %@",key);
}
思考
通过 KVC 修改属性会触发 KVO 吗?
会的。 不管有没有 set 方法都会触发。
可以看到即使我没有实现 set 方法,系统依然会触发 KVO 监听。
image.png
而当我们手动实现 willChangeValueForKey
和 didChangeValueForKey
可以发现,这两个方法是被调用了的。
image.png
可以看出,SDK 在找不到 setAge
和 _setAge
时,调用了 _NSSetValueAndNotifyForKeyInIvar
,而这个函数为我们调用了 willChangeValueForKey
和 didChangeValueForKey
。
image.png
感谢大佬
https://www.jianshu.com/p/45cbd324ea65
https://www.jianshu.com/p/247e78fa36c2