KVO的实现原理分析

先来介绍下KVO的基本概念:

KVO,即:Key-Value Observing,它提供一个机制,当指定的对象的属性发生了改变,则对象就会接收到通知。

也就是说它能时刻监听到属性的改变。

我们先来看个简单的例子:

首先,我创建了一个Student类,它有一个name属性:

#import <Foundation/Foundation.h>

@interface Student : NSObject

@property (nonatomic, copy) NSString *name;

@end
然后,我设计的是点击模拟器的屏幕,name后面的数字就会增加1,就会造成name的改变,紧接着在接受变更通知的

方法中打印出name的值,看是否能够监听到,代码如下:

#import "ViewController.h"
#import "Student.h"

@interface ViewController ()

@property (nonatomic, strong) Student *student;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Student *student = [[Student alloc] init];
    self.student = student;
    
    //利用KVO监听Student类的name属性变化
    [student addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
}

//收到属性变更的通知,就会调用此方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    
    NSLog(@"%@", self.student.name);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    static NSInteger i = 0;
    //每点击一次屏幕i就会+1
    NSString *name = [NSString stringWithFormat:@"Ocean - %zd", i++];
    self .student.name = name;
}

@end
运行程序,连续点击模拟器的屏幕,打印结果如下:

可以看出能够监听到name值的每次改变。

这是怎么造成的呢?

通过我们知道的知识,每次进行属性的更改,都会调用它的set方法,KVO的监听会不会跟set方法有关?

带着这个疑问,我们接着探讨。

此时设计成对属性的改变,使它不调用set方法,看是否能够监听到属性的改变:

1.把Student类的name属性设置成公有的,可以供外部调用

#import <Foundation/Foundation.h>

@interface Student : NSObject
{
    @public
    NSString *_name;
}

@property (nonatomic, copy) NSString *name;

@end
2.重写name的set方法,通过打印输出的方式知道set方法是否调用

#import "Student.h"

@implementation Student

- (void)setName:(NSString *)name {
    _name = name;
    
    NSLog(@"set方法被调用了");
}

@end
3.更改修改name值的方式,用  -> 方式来修改,达到不调用set方法的目的,所以我只需修改上面的touchsBegan方法即可

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    static NSInteger i = 0;
    //每点击一次屏幕i就会+1
    NSString *name = [NSString stringWithFormat:@"Ocean - %zd", i++];
//    self .student.name = name;
    //直接通过成员属性来修改name,不会调用其set方法
    self.student -> _name = name;
}
做了上述修改后,运行程序,点击模拟器屏幕,控制台没有任何输出。

所以我们可以得出一个结论:KVO的本质就是监听对象有没有调用set方法

那么又有一个问题,它是怎么监听有没有调用set方法呢?

仔细想下也不难想到,我们在开发中监听一个方法有没有调用,都是用到重写这个方法

那么官方是怎么重写这个方法的呢?我们把代码恢复到以前的方式,通过断点的形势来观察它的isa指针:
 

运行程序,观察它的isa指针:


图示部分很容易观察到,系统内部自动创建了一个NSKVONotifying_student的类,来重写方法,达到监听的目的,

然后在重写的方法中把通知发出去。

总结如下:

1.自定义一个NSKVONotifying_Studet的子类

2.重写setName方法,在里面通知观察者

3.修改isa指针,指向NSKVONotifying_Studet类,好让外界调用这个子类。

为了能更好的看懂我写的博客,我尽量每次都贴出源码,源码都在我的GitHub中,需要源码的点击这里

后面我会介绍运用runtime来自定义KVO,敬请期待...


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值