KVC 详解

定义

全称 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,_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 *)dictionaryWithValuesForKeys:(NSArray *)keys;
//输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。

原理

赋值原理

- (void)setValue:(nullable id)value forKey:(NSString *)key;  
  • 调用 setValue:forKey:
  • 触发 setKey:,
    • 如果setKey:不存在,则会寻找 _setKey:
    • 如果这两个方法都没有找到,会调用 accessInstanceVariablesDirectly,
      • 如果返回 NO, 意味着不允许访问成员变量,然后会调用 setValue:forUndefineKey,并抛出异常。
      • 如果返回 YES,会去根据 _key_isKeykeyisKey 的顺序去修改,如果找到,则对成员变量进行赋值,如果没有找到,会调用 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: '[ 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

而当我们手动实现 willChangeValueForKeydidChangeValueForKey 可以发现,这两个方法是被调用了的。

image.png

可以看出,SDK 在找不到 setAge_setAge 时,调用了 _NSSetValueAndNotifyForKeyInIvar,而这个函数为我们调用了 willChangeValueForKeydidChangeValueForKey

image.png

感谢大佬

https://www.jianshu.com/p/45cbd324ea65
https://www.jianshu.com/p/247e78fa36c2

你可能感兴趣的:(KVC 详解)