iOS爱上底层-KVC的原理分析

465 阅读5分钟

KVC简介

KVC(Key Value Coding)键值编码:一个基于NSKeyValueCoding非正式协议实现的机制,它可以直接通过key值对对象的属性进行存取操作,而不需通过调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性,而不是在编译时确定。

KVC-Setter的原理

根据官方文档的说明,我们可以得知KVC的取值过程:
1、首先会去查找set<Key>_set<Key>,如果找到了,则会根据Key进行设置
2、如果没找到,则会去查找类方法accessInstanceVariablesDirectly,如果类方法返回YES,则会按顺序查找_<key>_is<Key><key>is<Key>的实例变量,如果找到了,则根据相应的实例变量进行设值
3、如果都没找到,就回调用setValue:forUndefinedKey:,并且类方法accessInstanceVariablesDirectly返回NO,就会抛出一个异常

KVC-Setter的实践

实践set<Key>_set<Key>(上述文档步骤1)

实践类方法accessInstanceVariablesDirectly返回YES,且没有实现set<Key>_set<Key>方法,测试会不会去找相应的实例变量
首先在Person创建相应的实例变量(上述步骤2)

实践_<key>

_is<Key>

<key>

is<Key>

实践类方法accessInstanceVariablesDirectly返回NO,且没有实现set<Key>_set<Key>方法,测试会不会抛出异常(上述步骤3)

KVC-Getter的原理

根据官方文档,我们可以得知Getter的过程:
1、依次搜索实例方法,get <Key><key>is<Key>_<key>。 如果找到,则调用它并执行步骤5。 否则继续下一步

2、如果未找到简单的方法,则会去查找countOf <Key>objectIn <Key> AtIndex :(对应于NSArray类定义的原始方法)和<key> AtIndexes :(对应于模式)的方法。 NSArray方法objectsAtIndexes :)。 如果找到其中的第一个,再找到其他两个中的至少一个,则创建一个响应所有NSArray方法的集合代理对象,并返回该对象。否则,请继续执行步骤3。 代理对象随后将接收到的所有NSArray消息转换为countOf <Key>objectIn <Key> AtIndex:<key> AtIndexes:消息的某种组合,以创建键值编码的对象。如果原始对象还实现了一个名为get<Key>:range:之类的可选方法,则代理对象也将在适当时使用该方法。实际上,代理对象与与键值编码兼容的对象一起使用,可以使基础属性的行为就像是一个NSArray,即使不是NSArray。

3、如果未找到简单的访问器方法或数组访问方法组,则查找名为countOf <Key>enumeratorOf<Key>memberOf<Key>的三重方法:(对应于NSSet类定义的原始方法)。 如果找到所有三个方法,请创建一个响应所有NSSet方法的集合代理对象,并返回该对象。 否则,请继续执行步骤4。 此代理对象随后将其收到的任何NSSet消息转换为countOf <Key>enumeratorOf <Key>memberOf <Key>:消息的某种组合,以创建它的对象。 实际上,代理对象与与键值编码兼容的对象一起使用,使基础属性的行为就好像它是NSSet一样,即使不是NSSet。

4、如果找不到简单的访问器方法或集合访问方法组,并且接收者的类方法accessInstanceVariablesDirectly返回YES,则搜索名为_ <key>_ is <Key><key>is <Key>的实例变量, 以该顺序。 如果找到,请直接获取实例变量的值,然后继续执行步骤5。否则,请继续执行步骤6。

5、如果检索到的属性值是对象指针,则只需返回结果。 如果该值是NSNumber支持的标量类型,则将其存储在NSNumber实例中并返回它。 如果结果是NSNumber不支持的标量类型,请转换为NSValue对象并返回

6、如果所有其他方法均失败,则调用valueForUndefinedKey:。 默认情况下,这会引发一个异常,但是NSObject的子类可以提供特定于键的行为。

KVC-Getter的实践:

实践get <Key><key>is<Key>_<key>(上述步骤1):

get <Key>

<key>

is<Key>

_<key>

实践countOf <Key>objectIn <Key> AtIndex(步骤2)。

实践countOf <Key>enumeratorOf<Key>memberOf<Key>(步骤3)。

实践则搜索名为_ <key>_ is <Key><key>is <Key>(步骤4)

总结

1、setValue:forKey:首先会去查找set<Key>_set<Key>方法,如果没有且accessInstanceVariablesDirectly返回YES,则会去查找_<key>_is<Key><key>is<Key>的实例变量。如果返回NO,则会抛出一个异常。
2、valueForKey:首先会去查找get <Key><key>is<Key>_<key>,如果没有,则会去判断是否属于数组或者NSSet,如果属于,则会走相应的方法并返回对应的结果,如果不属于,则会查找_ <key>_ is <Key><key>is <Key>属性变量,找到了并返回该属性变量的值,没找到就回调用valueForUndefinedKey:并抛出异常
3、valueForKey:如果返回的值属于NSNumber类型,他会转成NSNumber对象,否则会转成NSValue对象进行返回。