KVC

598 阅读3分钟

KVC 键值编码,是一种 NSKeyValueCoding非正式协议启用的一种机制。对象采用协议来间接访问对象属性。

1、通过 key 设值或取值

  • setValue: forfey:
  • setValue: forKeyPath:

2、通过 keyPath 取值

  • valueForKey:
  • valueForKeyPath:

底层实现

setKey:value: 设值

  1. 查找是否有 setKey,_setKey,_setIsKey 三种 set方法,key 指成员变量
  2. 如果这三种key都没有找到,则去查找 accessInstanceVariablesDirectly 是否返回 YES (默认返回YES),如果为 yes,则会查找直接访问的实例变量进行赋值,按照 _key,_iskey,key,iskey 的顺序搜索成员
  3. 如果没有找到 或者 accessInstanceVariablesDirectly 返回 NO,说明 setter方法和实例变量都没有找到,系统会执行 setValue:forUndefinedKey: 方法,如果该方法没有实现会抛出 NSUndefinedKeyException 类型异常

image.png

valueForKey: 取值

  1. 先查找 getter 方法,按照 getKey,key 的顺序查找,如果找到执行第 5 步,否则执行第二步
  2. 查找 countOfKey,objectInKeyAtIndex,以及 keyAtIndexes
  • 如果找到 countOfKey和另外两个中的任意一个方法,则会创建一个响应所有 NSArray 的方法集合代理对象,并返回该对象,即 NSKeyValueArray,是 NSArray 的子类。
  • 代理对象随后将收到的所有 naraary消息转换为 countOfKey,objectInKeyAtIndex 和 keyAtIndexes: 消息的某种组合,用来创建键值编码对象。
  • 如果原始对象还实现了一个名为 getKey:range: 之类的可选方法,则代理对象也将在适当时使用该方法(注意:方法名的命名规则要符合 KVC 的标准命名方法,包括方法签名。)如果没有找到则进入第三部:
  1. 上面的方法都没有找到
  • 此时会同时查找 countOfKey,enumeratorOfKey 和 memberOfKey 这三个方法。
  • 如果这三个方法都找到,则会创建一个响应所有NSSet方法的集合代理对象,并返回该对象,此代理对象随后将其收到的所有NSSet消息转换为countOfKey enumeratorOfKey memberOfKey 消息的某种组合,用于创建它的对象。
  • 如果没有找到则进入第四步。
  1. 检查类方法 accessInstanceVariablesDirectly 是否为 YES,如果为YES,查找成员变量 _key,_iskey,key,iskey,如果找到 key 则进入第 5 步,如果没有找到或者accessInstanceVariablesDirectly 为 NO 则进入第 6 步
  2. 根据找到的属性值类型,返回不同的结果,如果是指针类型,直接返回结果;如果是NSNumebr 支持的标准类型,则将其存储在 NSNumebr 实例中,返回该值;如果都不是,则转换成 NSValue 类型,并返回。
  3. 说明没有找到对应的 key,此时会执行 valueForUndefinedKey 方法,如果该方法未实现,则抛出 NSUndefinedKeyException 类型的异常。 image.png

自定义 KVC 赋值

自定义 set 方法的思想:

1.先判断key值是否存在,不存在就没必要继续,直接返回。

2.查找setter方法,顺序为setKey、_setKey、setIsKey。

3.判断accessInstanceVariablesDirectly是否为YES,为YES继续下面的流程,为NO就直接抛出异常。

4.间接访问变量赋值(只赋值一次),赋值顺序:_Key、_isKey、Key、isKey。

  • 定义一个收集实例变量的可变数组
  • 通过class_getInstanceVariable获取相应的ivar
  • 通过object_setIvar,对相对应的ivar进行赋值
  1. 如果找不到相关实例变量,则抛出异常

image.png

自定义 get 方法的思想:、

1.判断key,必须非空。

2.查找相应的方法,顺序:getKey、Key、countOfKey、objectInKeyAtIndex: (key都为传入的key值)。

3.判断accessInstanceVariablesDirectly是否为YES(是否能够直接赋值实例变量,YES是可以)。为NO则抛出异样。(抛出异常就为崩溃)。

4.间接访问实例变量,顺序:_Key、_isKey、Key、isKey

  • 定义一个收集实例变量的可变数组
  • 通过class_getInstanceVariable,获取相应的ivar
  • 通过object_getIvar,返回相对应ivar值

image.png