iOS笔记 - KVC实现原理


KVC实现原理

1 - 我们知道使用 KVC赋值会触发 KVO,比如 setValue: forKeyPath:方法实现原理如图所示

接下来我们就一步步进行验证

// - Person.h

 1 #import 
 2 @interface Person : NSObject{
 3     @public
 4     NSString *_name;
 5     
 6     // 验证 KVC访问成员变量的机制
 7     // int _age;
 8     // int _isAge;
 9     // int age;
10     int isAge;
11 }
12 
13 @end

// - Person.m

 1 #import "Person.h"
 2 @implementation Person
 3 
 4 // KVC 赋值步骤
 5 
 6 // 步骤一:
 7 // 存在 setAge方法就调用该方法
 8 //-(void)setAge:(int)age{
 9 //
10 //    NSLog(@"启用 setAge");
11 //
12 //    _age = age;
13 //}
14 
15 
16 // 步骤二:
17 // 不存在 setAge方法就调用该方法
18 //-(void)_setAge:(int)age{
19 //
20 //    NSLog(@"启用 _setAge");
21 //    _age = age;
22 //}
23 
24 // 步骤三:setAge、_setAge两方法都不存在,则调用该方法
25 // 是否允许去访问成员变量
26 + (BOOL)accessInstanceVariablesDirectly{
27     
28     
29     // 情况一
30     // 默认 YES
31     return YES; // 则去访问成员变量
32     
33     // 查询成员变量的顺序:依次 _key、_isKey、key、isKey
34     // 该示例中就是           _age、_isAge、age、isAge
35     // 如果没有查到成员变量,这进入 步骤四
36     
37     // 情况二
38     // return NO; //则直接 步骤四
39     
40 }
41 
42 // 步骤四:调用该方法并抛出 NSUnknownKeyException异常
43 - (void)setValue:(id)value forUndefinedKey:(NSString *)key{
44     
45 }
46 
47 @end

// - TObServer.m:新建 TObServer继承自 NSObject,实现监听方法

1 #import "TObServer.h"
2 @implementation TObServer
3 
4 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionaryid> *)change context:(void *)context{
5 
6     NSLog(@"%@  ---  %@",keyPath,change);
7 }
8 
9 @end

// - ViewController.m

 1 #import "ViewController.h"
 2 #import "Person.h"
 3 #import "TObServer.h"
 4 @interface ViewController()
 5 
 6 @end
 7 
 8 @implementation ViewController
 9 
10 - (void)viewDidLoad {
11     [super viewDidLoad];
12     self.view.backgroundColor = [UIColor cyanColor];
13     
14     TObServer *observer = [TObServer new];
15     Person *p1 = [Person new];
16     
17     [p1 addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
18     
19     // 方式一
20     // [p1 setValue:@12 forKey:@"age"];
21     
22     // 方式二:根据路径赋值,功能更强大
23     [p1 setValue:@21 forKeyPath:@"age"];
24     
25     // 注意
26     // 状况一:如果存在 setter方法(_setAge:/setAge),则触发 KVC我们很好理解
27     // 其实就是 setValue:forKeyPath: 去调用了 setter方法
28     // 并且调用 willChangeValueForKey、didChangeValueForKey触发监听
29     
30     
31     // 状况二:如果不存在 setter方法,只存在成员变量,那么依旧可以触发监听
32     // 就是说 setValue:forKeyPath会寻找 setter方法,没有的话就寻找其成员变量
33     // 只要可以找到任何一种结果,就会启动 willChangeValueForKey、didChangeValueForKey触发监听
34     
35     [p1 removeObserver:observer forKeyPath:@"age"];
36 }
37 
38 @end

日志信息:只存在成员变量依旧触发监听

2 - KVC取值原理(不再一步步验证)