先来介绍下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,敬请期待...