原创文章,转载请注明:转自:Try_Try_Try
背景
关于KVC的使用方法,网络上已有很多文章介绍,这篇文章讲解的也很全面(https://www.jianshu.com/p/45cbd324ea65)。最近被问到了很多的问题,因此会将问到的问题都总结一下,方便之后查看。
很简单的一句话,描述一下kvc的get方法调用顺序?
嗯哼,我伙呆,当时就火大。
吐槽一下:感觉记住这个顺序用处不太大,可能当时记住了,之后就又忘了。我其实是比较反感问一些需要死记硬背,但是平时用处不太大的问题。如果当时有电脑,我直接可以进行演示操作,验证推倒。我直接说了句:忘了(对面坐的神 笑了一下,我内心开始不淡定,摸了摸后脑勺和鼻子,低下了头。)
目录结构:
-
- 代码结构简介
- 1.1 类结构
- 1.2 代码展示
-
- KVC中get调用顺序
- 2.1 观察图片总结结论
- 2.2 验证
- KVC中set调用顺序
正文:
因为我们对于图片都是敏感的,所以文章中尽量给出图片方便记忆(就像我开头放的那张小魔仙的图片一样,现在你一定还记得---内心笑一下吧)。
1. 代码结构简介
1.1 类结构
类结构1.2 代码展示
其中Animate.h 和 Plant.h 的 实例变量是一样的。
Animate.h
#import <Foundation/Foundation.h>
// 记录get
@interface Animate : NSObject{
// @public
// NSString *_age;
// NSString *_isAge;
// NSString *age;
// NSString *isAge;
}
@end
Animate.m
#import "Animate.h"
@implementation Animate
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
- (NSString *)getAge
{
NSLog(@"%@", NSStringFromSelector(_cmd));
return @"1";
}
- (NSString *)age
{
NSLog(@"%@", NSStringFromSelector(_cmd));
return @"2";
}
- (NSString *)isAge
{
NSLog(@"%@", NSStringFromSelector(_cmd));
return @"3";
}
- (NSString *)_getAge
{
NSLog(@"%@", NSStringFromSelector(_cmd));
return @"4";
}
- (NSString *)_age
{
NSLog(@"%@", NSStringFromSelector(_cmd));
return @"5";
}
@end
Plant.h
#import <Foundation/Foundation.h>
// 记录set
@interface Plant : NSObject{
// @public
// NSString *_age;
// NSString *_isAge;
// NSString *age;
// NSString *isAge;
}
@end
Plant.m
#import "Plant.h"
@implementation Plant
- (void)setAge:(NSString *)age
{
NSLog(@"%@--%@", NSStringFromSelector(_cmd), age);
}
- (void)_setAge:(NSString *)age
{
NSLog(@"%@--%@", NSStringFromSelector(_cmd), age);
}
@end
2. KVC中get调用顺序
好吧,先来一张图,说明执行的顺序,接着验证
2.1 观察图片总结结论
图片
图2.1 getValueForkey:从上图2.1中可以看出基本的调用顺序,关于为什么是这个流程,其实通过断点验证出来的,并没有从源代码中发现其具体实现过程(关于这点,我一直在查找资料,毕竟要见到本质才相信)。
- 右边的方法,左边的字段;(先调用方法,再执行字段)
- 方法调用顺序:getKey、key、isKey、_getKey、_key
- 字段调用顺序:_key、_isKey、key、isKey
2.2 验证
查看文件Animate相关内容
main.m中添加如下内容
int main(int argc, const char * argv[]) {
@autoreleasepool {
// get
Animate *animate = [[Animate alloc] init];
animate->_age = @"1";
animate->_isAge = @"2";
animate->age = @"3";
animate->isAge = @"4";
NSString *age = [animate valueForKey:@"age"];
NSLog(@"Hello World!");
}
return 0;
}
- 如果将Animate.h和Animate.m中的注释都放开。在
NSLog(@"Hello World!");
该行打印断点,可以验证出来先执行方法,再执行属性; - 验证方法的顺序:先把.h中字段注释,main.m中的字段赋值也进行注释,只留下Animate.m中的几个方法如下:
- (NSString *)getAge
{
NSLog(@"%@", NSStringFromSelector(_cmd));
return @"1";
}
- (NSString *)age
{
NSLog(@"%@", NSStringFromSelector(_cmd));
return @"2";
}
- (NSString *)isAge
{
NSLog(@"%@", NSStringFromSelector(_cmd));
return @"3";
}
- (NSString *)_getAge
{
NSLog(@"%@", NSStringFromSelector(_cmd));
return @"4";
}
- (NSString *)_age
{
NSLog(@"%@", NSStringFromSelector(_cmd));
return @"5";
}
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
运行后,发现只打印getAge
, 接着注释getAge
方法,发现打印的其他方法。依次重复几次,就能得出一个顺序:getKey、key、isKey、_getKey、_key
- 验证公有字段的执行顺序:通过断点查看给哪个字段进行了赋值。然后注释该字段,依次操作,就可得到执行的顺序:_key、_isKey、key、isKey
- accessInstanceVariablesDirectly方法默认是返回YES,从方法名称也可以看出来该方法的含义,它用来控制实例变量是否可以访问。
3. KVC中set调用顺序
同样再来一张图,说明执行的顺序,接着验证
图3.1 setValue:forKey:
从图3.1中可以发现和图2.1中左边的实例变量的调用顺序是一样的。只是方法调用少了几个。
结论
- 右边的方法,左边的字段;(先执行方法,再执行字段)
- 方法调用顺序:setKey、_setKey
- 字段调用顺序:_key、_isKey、key、isKey
验证
查看文件Plant相关内容
main.m中有如下内容:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// set
Plant *plant = [[Plant alloc] init];
[plant setValue:@"2" forKey:@"age"];
NSLog(@"Hello World!");
}
return 0;
}
- 验证的方法和上述get的操作类似,就会得出上图的结果。这里请自行验证。
其中Plant.m中的方法如下:
- (void)setAge:(NSString *)age
{
NSLog(@"%@--%@", NSStringFromSelector(_cmd), age);
}
- (void)_setAge:(NSString *)age
{
NSLog(@"%@--%@", NSStringFromSelector(_cmd), age);
}
通过验证,就会和图3.1总结的一样。
关于更高深的其他操作这里不再做分析,只对其基本原理进行剖析。
- 本文相关的demo已放置github.
- 在阅读中,如果发现文章有不合理的地方,欢迎提出疑问至邮箱:B12050217@163.com.
- 原创文章,转载请注明:转自:Try_Try_Try
- OK, game over.
网友评论