前两篇文章主要讲解了KVC的基本使用和赋值操作的实现,今天我们来看一下 是如何通过key进行取值的。
其步骤大致如下:
1、首先会在接收者的方法列表中,依照 getKey()、key()、isKey()、_key()的顺序,去寻找方法的实现,找到其中一个的话,将其值返回给key对应的属性。
2、假如第一步查找失败,会在方法列表中查找
a: -countOf<Key>:(id)key 、
b: -indexIn<Key>OfObject:()
c: -objectIn<Key>AtIndex:(NSInteger)index、
d: -<key>AtIndexes:(NSInteger)index。
如果找到方法a和b,并且c 和 d至少一个方法,会返回一个NSKeyValueOrderedSet类型的对象A。
当调用集合对象A的count()/objectAtIndex/indexOfObject 等集合方法时,就会调用我们方法列表中实现的对应方法,并将其值返回。
3、假如第二步没有找到对应的方法,只是找到了方法a和c/d中的至少一个方法,会返回一个NSKeyValueArray类型的对象。并执行对应的集合对象操作,类似于第二步。
4、加入第三步查找失败,会继续查找以下三个方法:
a: -countOf<Key>:(id)key;
b:-enumeratorOf<Key>;
c:-memberOf<Key>:
如果三个方法全都存在,会返回一个NSKeyValueSet类型的集合对象,当调用集合的方法时,会调用以上三个方法的实现。
5、当第四步执行失败,就会访问 +accessInstanceVariablesDirectly(), 当该方法返回YES时,会从对象的成员方法列表中以 _<key>, _is<Key>, <key>, or is<Key>的顺序进行查找匹配,如果找到则返回对应的值。
6、如果第五步查找失败,会调用 -valueForUndefinedKey(),方法,该方法默认会抛出NSUndefinedKeyException异常,导致程序崩溃。我们可以通过重写此方法,避免程序崩溃。
以上6步就是通过KVC取值的全部过程。
下面我们通过代码来验证。
首先验证步骤一:以height属性为例,在Person类中实现getHeight(),height(),isHeight()和_height()方法。
当我们依次将方法注释后,得到的结果为:111、222、333、444。证明按顺序取值,找到对应方法后,即返回给valueForKey()。
步骤二:假如没有实现以上四个方法,我们以numbers属性为例,声明并实现以下四个方法:
调用valueForKey方法,得到NSKeyValueOrderedSet类型实例,与步骤二结果吻合:
假如我们没有实现numbersAtIndexes:() 或者 objectInNumbersAtIndex()方法,同样的调用,结果也是一样的。
步骤三:假如只实现了-(NSUInteger)countOfNumbers;,同时实现了-(id)objectInNumbersAtIndex:(NSUInteger)index;和- (id)numbersAtIndexes:(NSInteger)index;中的至少一个,再次调用会得到如下结果:
步骤四:假如在接收者中只有以下三个方法实现:
同样的调用会得到类NSKeyValueSet的实例,与步骤4推论吻合。
步骤五:仍旧以numbers为例,在Person类中声明四个成员变量,并对其赋值:
可以依次将_key,_isKey,key,isKey方法注释,查看结果进行验证。
注意+ (BOOL)accessInstanceVariablesDirectly()默认返回值为YES,如果我们重写其方法将其返回NO时,会触发-valueForUndefinedKey()方法。
步骤六:如果+ (BOOL)accessInstanceVariablesDirectly()依旧返回YES,但是并没有找到对应的属性时,依旧会触发-valueForUndefinedKey()方法。
以上就是valueForKey:(NSString*)key 的全部调用过程。中间省去了一些调试变化的过程,各位朋友可以调试各种情况试一下,相信会有新的发现。
虽然按照文档,将整个调用过程用代码翻译了一遍,但是在这里,笔者仍有一个疑问,在通过成员变量取值之前,为什么会先去尝试判断集合属性,这样的设计,是为了什么?有知晓答案的朋友吗,希望能够一起探讨。
网友评论