KVC浅析

245 阅读4分钟

KVC基础

KVC是一种通过键值编码的方式间接访问对象属性的机制,可以通过字符串访问对应的属性方法或成员变量。

比如:我们经常使用的字典赋值就是依赖于KVC。

成员变量

成员变量通常是指那些基础类型或常量。

实例变量

是一种特殊的成员变量,我们通过Class类进行实例化出来的变量就是实例变量。

UIButton *btn;
id hello 是Oc一种特殊的class,不一定是实例变量  

属性

默认setter和getter方法 苹果早期的编译器是GCC,后期升级编译器也就是我们现在使用LLVM。 当没有匹配实例变量的属性,LLVM就会自动创建一个带下划线的实例变量,并生成setter和getter方法.

KeyPath

除了可以对当前的属性进行赋值外,我们还可以对其对象更深层次的属性进行赋值。

@property (weak, nonatomic) IBOutlet UITextField *textFiled; 
[self.textFiled setValue:[UIColor orangeColor] forKeyPath:@"_placeholderLabel.textColor"];

赋值和取值过程

取值过程:

KVC提供的getter默认方法valueForKey:,当给出参数key的时,进行方法调用,接下来是Getter内部执行流程如下:

一、普通类型

  1. get(Key)
  2. (Key)
  3. is(Key)
  4. _(Key) 按照以上的顺序的拼接进行实例方法的搜索,如果找到符合的方法 ,就调用对应的方法并拿出结果goto至第四步;反之,进行下一步操作。

二、集合类型

  1. 数组array
  • countOf(Key)
  • objectIn(Key)AtIndex:
  1. 集合set
  • countOf(Key)
  • enumeratorOf(Key)
  • memeberOf(Key) 如果没有找到,则继续搜索其匹配的方法的,定义规则是按照以上集合类型顺序(先搜索array,再搜索set)。 如果找到其中的任意一个,则创建一个集合代理对象,该对象响应所有集合类型的方法并返回。反之,进行下一步操作。

三、accessInstaceVariablesDirectly 是否开启间接访问,默认返回YES

  1. _(Key)
  2. _is(Key)
  3. (Key)
  4. is(Key)

如果开启间接访问,会按照以上顺序的拼接方法进行搜索,如果发现对应的实例,则进下一步操作,反之,goto到第六步。

四、结果返回

  1. 对象则直接返回
  2. 基础数据类型,支持NSNumber的话,直接返回NSNumber类型
  3. 不支持NSNumber的基础数据类型,直接返回NSValue类型

五、没有找到 通过以上方法进行搜索,都没有匹配到对应的实例,则默认会调用valueForUndefineKey:方法并抛出异常。

赋值过程

两种类型

基础 setter 搜索模式

KVC提供的setter默认方法setValue:forKey:,当给出参数key和value的时,进行方法调用,接下来是setter内部执行流程如下:

  1. 首先会找set(Key): 或者 _set(Key)的拼接的setter,如果找到,调用这个方法并进行赋值操作,反之,进行下一步操作
  2. 判断是否间接访问 accessInstaceVariablesDirectly
  • _(Key)
  • _is(Key)
  • (Key)
  • is(Key) 如果开启间接访问,则会按照以上顺序进行拼接的实例变量,如果找到,将value赋值给实例变量
  1. 如果没有找到,就会报错,默认调用setValue:forUndefinedKey:方法并抛出异常

Mutable Arrays 搜索模式

此模式KVC提供默认实现方法mutableArrayValueForKey:,当给出key参数时, 集合类型

  1. 首先会找insertObject:in(Key)AtIndex: or insert(Key):atIndexes:和removeObjectFrom(Key)AtIndex: or remove(Key)AtIndexes:方法,如果找到返回一个代理对象来响应发送给NSMutableArray的组合消息;当对程序时行性能分析发现问题时,可以实现替换方法:removeObjectAtIndex: and removeObjectsAtIndexes:来进行处理。
  2. 如果没有找到可变数组方法,也没有找到访问器,查找一个set(Key)格式的方法,向mutableArrayValueForKey:的原始响应者发送set(Key):消息
  3. 如果没可变数组方法,也没有找到访问器,当开启间接访问accessInstanceVariablesDirectly,则查找一个命名为_(Key)或(Key)的实例变量,按这个顺序,如果找到实例变量,则返回一个代理对象。
  4. 如果都没有找到,刚返回一个可变集合代理对象。当它接收NSMutableArray消息时,发送一个setValue:forUndefineKey:给消息的原始对象并抛出NSUndefineKeyException异常。

注:还有NSMutableSet和NSMutableOrderedSet两种搜索模式步骤同上,只搜索和调用的方法不一样,具体可以参考官方文档:Key-Value Coding Programming Guide

KVC异常处理

根据KVC搜索流程,没有查找对应的key,则会调用对应的异常方法。异常方法的默认实现,在异常发生时会抛出一个NSUndefineKeyException的异常,并且应用闪退。 我们可以重写下面两个方法,根据业务需求处理KVC导致的异常

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

如果通过KVC给某个非对象的属性赋值nil时,此时KVC会调用属性所属对象的setNilValueForKey:方法,并抛出NSInvalidArgumentException的异常,并且应用闪退。 我们通过重写下面方法,在发生这种异常时进行处理。

- (void)setNilValueForKey:(NSString *)key {
    if ([key isEqualToString:@"name"]) {
        [self setValue:@"" forKey:@"age"];
    } else {
       [super setNilValueForKey:key]; 
    }
}

以上部分内容参考:

  1. www.jianshu.com/p/1d39bc610…