KVC底层原理分析

910 阅读4分钟
  • KVC简介

    KVC全称是Key Value Coding,定义在NSKeyValueCoding.h文件中,是一个非正式协议。KVC提供了一种间接访问其属性方法或成员变量的机制,可以通过字符串来访问对应的属性方法或成员变量。
  • KVC常用方法

    KVC方法如下图:
    • Getting Values
      • valueForKey:
        返回key对应的属性值 用法[person valueForKey:@"array"]
      • valueForKeyPath:
        同样是返回key对应的属性值,但是这里需要传递key的路径 valueForKey相当于传入相对路径,valueForKeyPath相当于传入绝对路径。
        举例:
        想获取student类中的对象的值,[person valueForKeyPath:@"student.subject"]
      • dictionaryWithValuesForKeys:
        返回一个字典,其中包含由给定数组中的每个键标识的属性值。
      • valueForUndefinedKey:
        当调用valueForKey方法是key在类的属性中没有找到则会调用该方法,可在类中重写该方法,如果没有重写的话则会抛出异常
      • mutableArrayValueForKey:
        返回一个可变数组代理,该代理提供对给定键指定的数组(包含不可变数组)的读写访问。 用处在于如果一个NSArray的一个属性如果想修改其中的值可以通过mutableArrayValueForKey返回一个可变数组然后在对数组中的元素进行读写
      • mutableArrayValueForKeyPath: 作用同valueForKeyPath同样的传入key值的绝对路径
      • mutableSetValueForKey:(不常用)
        返回一个可变集合代理,该代理提供对给定键指定的无序对多关系的读写访问。作用同mutableArrayValueForKey
      • mutableSetValueForKeyPath:(不常用)
        作用同valueForKeyPath
      • mutableOrderedSetValueForKey:(不常用)
        mutableArrayValueForKey
      • mutableOrderedSetValueForKeyPath:(不常用)
        mutableArrayValueForKeyPath
    • Setting Values
      • setValue:forKeyPath:
        将由给定键路径标识的属性的值设置为给定值。
        [person setValue:@"Swift" forKeyPath:@"student.subject"];
      • setValuesForKeysWithDictionary:
        给定一个字典,将字典中的所有key对应的属性value设置成字典中key对应的value
      • setNilValueForKey:
        如果调用setValue:forKey:方法给标量值(就是简单数据类型如int,float等)赋值的时候value传入nil时调用,如果在子类中重写了该方法会调用子类中的方法,如果没有重写则会抛出异常
      • setValue:forKey: 将给定键指定的接收者属性设置为给定值。
      • setValue:forUndefinedKey:
        用法原理类似valueForUndefinedKey,传入的key值找不到时调用如果子类重写了该方法则调用子类的方法反之则抛出异常
  • KVC实现原理

    原理探索冲最基础的setValue:forKey:valueForKey:入手其他方法都是大同小异,应为KVC的代码是闭源的所以我们只能通过看官方文档猜测苹果的内部实现机制
    • 取值流程分析
      观看官方文档可知分为五个步骤
      1. 先查找get<key>方法如果没有在查找<key>方法没有再找is<Key>方法_<key>方法如果找到一个则跳转到第三步,否则走第二步(注意如果是NSArray和NSSet下面还应该有两个步骤就是找到是否含有NSArray和NSSet的原始创建方法如果有则创建一个)
      2. 检查类方法accessInstanceVariablesDirectly是否返回YES,如果返回YES在一次查找实例变量_<key>_is<Key><key>is<Key>,如果能搜索则直接返回对应值否则跳转到第三步否则跳转到第四步
      3. 如果检索到的属性值是对象指针,则只需返回结果。
        如果该值是NSNumber支持的标量类型,则将其存储在NSNumber实例中,然后将其返回。 如果结果是NSNumber不支持的标量类型,请转换为NSValue对象并返回该对象。
      4. 执行valueForUndefinedKey方法,默认会抛出异常
      5. 流程图如下:
    • 设置流程分析
      1. 查找set<Key>:_set<Key>命名的setter,按照这个顺序,如果找到的话,调用这个方法并将值传进去(根据需要进行对象转换)。
      2. 如果没有发现一个简单的setter,但是accessInstanceVariablesDirectly类属性返回YES,则查找一个命名规则为_<key>_is<Key><key>is<Key>的实例变量。根据这个顺序,如果发现则将value赋值给实例变量
      3. 如果没有发现setter或实例变量,则调用setValue:forUndefinedKey:方法,并默认提出一个异常,但是一个NSObject的子类可以提出合适的行为。
      4. 流程图如下
    • 自定义实现KVC

      github地址:github.com/Bore-TuDou/…