上篇文章留了个彩蛋,我们只是声明了成员变量height 和 weight,但是我们通过 setValue: forKey成功对height 和 weight 进行了赋值操作,这背后究竟发生了什么,我们一起来看一下。
首先看官方API,- (void)setValue:(nullableid)valueforKey:(NSString*)key;
通过一个可以标识属性的标识符,给属性赋值,建立起键值一对一的关系,如果key之前有对应的值,则将其解约。
继续看文档,我们发现其默认的实现步骤是先去找 -set<Key>:方法,如果找到,会去检查参数是否为nil,如果为nil,会调用setNilValueForKey方法,默认的实现方式是抛出NSInvalidArgumentException异常,当然我们可以在类中重写此方法,来避免崩溃。
2.如果没有找到-set<Key>:方法,其实会退而求其次,寻找-_set<Key>:方法,如果有则调用-_set<Key>:进行赋值操作。
3.假如-set<Key>: 和 -_set<Key>:都没有找到的话,继续调用+accessInstanceVariablesDirectly,询问是否允许访问成员变量,这个方法系统默认返回YES,也就是可以访问成员变量。
假如我们重写了+accessInstanceVariablesDirectly方法,并返回NO,不允许访问成员变量时,系统会调用-setValue:forUndefinedKey:方法,此方法的默认实现会抛出NSUndefinedKeyException异常,当然我们可以在实现中重写此方法,来避免崩溃。
4.在得到了访问成员变量权限后,会按照_<key>, _is<Key>, <key>, or is<Key>的顺序查看是否有对应的成员变量,如果有则进行赋值。加入最后都没有找到,依旧调用-setValue:forUndefinedKey:方法。
以上就是 - (void)setValue:(nullableid)valueforKey:(NSString*)key 的调用过程,下面 我们通过代码来验证一下,由于系统会自动帮属性创建对应的set、get方法,我们依旧使用成员变量_weight来研究:
![](https://img.haomeiwen.com/i1672698/4bef26be972ef582.png)
设置set方法:
![](https://img.haomeiwen.com/i1672698/245b2165e92e5b6b.png)
进行调用:
![](https://img.haomeiwen.com/i1672698/641b9950d3940f42.png)
得到如下结果:
![](https://img.haomeiwen.com/i1672698/679b987408d28168.png)
由于我们分别实现了setHeight 和 _setHeight方法,在打印结果中,我们看到的是-setHeight方法被调用;对weight成员变量,我们只是实现了 _setWeight方法,因为没有找到setWeight方法,调用_setWeight()方法,和我们上面的分析结果吻合。
(二)当我们为 height 赋值 nil时,如果不做其他操作,系统会抛出异常,为了避免崩溃,只需要在PMPerson中重写-(void)setNilValueForKey:(NSString *)key,如图:
![](https://img.haomeiwen.com/i1672698/353eaf1dcaae4744.png)
此时再向height传递nil值,就会在控制台看到 “value is not Nil”的打印结果。
![](https://img.haomeiwen.com/i1672698/d1e3a85f83f162f8.png)
(三)加入我们不是想setKey和_setKey方法,同时将+(BOOL)accessInstanceVariablesDirectly返回值设置为nil,如图:
![](https://img.haomeiwen.com/i1672698/9c166f1706d278b0.png)
此时再去调用赋值操作,系统则会抛出NSUnknownKeyException异常。只需要重写- (void)setValue:(id)value forUndefinedKey:(NSString *)key即可,如图:
![](https://img.haomeiwen.com/i1672698/72d969fbeb3f2d7b.png)
(四)假如允许访问成员变量,就可以解开我们开篇时提到的问题了,为什么通过height和_height都可以进行赋值成功。
我们以weight为例,赋值语句如下:
![](https://img.haomeiwen.com/i1672698/5bc5ec64c17318eb.png)
按照上面的分析,此时只要成员变量中包含 __weight/_is_weight/_weight/is_weight,就可以赋值成功。我们的测试用例如下:
![](https://img.haomeiwen.com/i1672698/4a8e967be776f883.png)
看到这里,我们对- (void)setValue:(nullableid)valueforKey:(NSString*)key;的整个调用过程应该很熟悉了,如果觉得文字太绕了的话,可以看一下下面这幅图:
![](https://img.haomeiwen.com/i1672698/4d7459c4c9b53c2c.png)
再次感谢杰哥的精美制图。
网友评论