KVC(Key-Value Coding)

一、KVC简介

KVC:键值编码,使用字符串直接访问对象的属性

常用方法

@interface NSObject(NSKeyValueCoding)
 
- (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来设值
 
@end

NSKeyValueCoding其他方法

+ (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转到字典。

二、实现原理

setValue:forKey:原理

  1. 查找是否实现setter方法,如果有,优先调用setter方法完成赋值(注意:set后面的键的第一字字母必须是大写!!)
  2. 当没找到setter方法,调用accessInstanceVariablesDirectly询问;如果返回YES,顺序匹配变量名与__is,,is,匹配到则设定其值,如果返回NO,结束查找,并调用 setValue:forUndefinedKey:报异常
  3. 如果既没有setter也没有实例变量时,调用 setValue:forUndefinedKey:
    setValue:forKey:原理

valueForKey:的原理

  1. kvc取值按照 getKey、key、iskey、_key顺序查找方法
  2. 存在直接调用
  3. 没找到同样,先查看accessInstanceVariablesDirectly方法
  4. 如果可以访问会按照_key、_isKey、key、iskey的顺序查找成员变量
  5. 找到直接取值
  6. 如果所有情况都失败,则调用valueForUndefinedKey:方法并抛出NSUnkonwKeyException异常,这是默认行为,但是子类可以重写此方法。
valueForKey:原理

三、搜索规则

NSMutableArray 、NSMutableSetNSMutableOrderedSet对应的搜索规则
NSMutableSet为例说明:

- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;
NSMutableSet原理
  1. 搜索addObjectObject: , removeObject:或者 add , remove格式的方法
    如果至少找到一个insert方法和一个remove方法,那么同样返回一个可以响应NSMutableSet所有方法代理集合(类名是NSKeyValueFastMutableSet2),那么给这个代理集合发送NSMutableSet的方法,以addObjectObject: , removeObject:或者 add , remove组合的形式调用。还有两个可选实现的接口:intersect , set:

  2. 如果receiverManagedObject,那么就不会继续搜索

  3. 如果上一步的方法没有找到,则搜索set: 格式的方法,如果找到,那么发送给代理集合的NSMutableSet最终都会调用set:方法。

  4. 如果上一步的方法还没有找到,再检查类方法+ (BOOL) accessInstanceVariablesDirectly,如果返回YES(默认行为),会按_,,的顺序搜索成员变量名,如果找到,那么发送的NSMutableSet消息方法直接交给这个成员变量处理。

  5. 如果还是找不到,则调用valueForUndefinedKey:

四、适用场景

  1. 动态取值与设值
  • setValue:forKeyPath:valueForKeyPath:可以嵌套层级
  • setObject:forKey:ObjectForKey:中的key可以不为NSString
  • setObject:forKey:中的object不能为空,但是另外两者可以
  1. 字典转模型
  2. 访问或修改私有变量,iOS13之后苹果限制了此方法

例如在.m中定义的私有成员变量和属性,都可以通过KVC的方式访问。
这个操作对readonly的属性,@protected的成员变量,都可以正常访问。如果不想让外界访问类的成员变量,则可以将accessInstanceVariablesDirectly属性赋值为NO

  1. 修改一些控件的内部属性

众所周知很多UI控件都由很多内部UI控件组合而成的,但是Apple度没有提供这访问这些控件的API,这样我们就无法正常地访问和修改这些控件的样式。而KVC在大多数情况可下可以解决这个问题。

五、KVC异常处理

  1. key或者keyPath发生错误

当根据KVC搜索规则,没有搜索到对应的key或者keyPath,则会调用对应的异常方法。异常方法的默认实现,在异常发生时会抛出一个NSUndefinedKeyException的异常,并且应用程序Crash
我们可以重写下面两个方法(这种方法只能运用在自己类的对象中):

- (nullable id)valueForUndefinedKey:(NSString *)key;
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

  1. 传参为nil

通常情况下,KVC不允许你要在调用setValue:属性值 forKey:(或者keyPath)时对非对象传递一个nil的值;如果你不小心传了,KVC会调用setNilValueForKey:方法。这个方法默认是抛出异常,所以一般而言最好还是重写这个方法。

我们可以重写这个方法:

-(void)setNilValueForKey:(NSString *)key{
    NSLog(@"不能将%@设成nil",key);
}
  1. 使用swizzling

你可能感兴趣的:(KVC(Key-Value Coding))