了解一下KVO的底层实现原理
1. KVO 是基于 RunTime 机制实现的
2. 当某个类的对象第一次被观察时,系统就会在运行期动态的创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter方法.派生类在被重写的setter方法实现真正的通知机制 (Person → NSKVONotifying Person)
代码实现部分
1.首先创建一个Person类和一个Dog 类 (类名可以随便) 并在Person类中声明一个age 属性来做被监听的属性
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, assign) NSInteger age;
@end
2.在ViewController的.m 中实现以下代码
#import "ViewController.h"
#import "Person.h"
#import "Dog.h"
@interface ViewController ()
@property (nonatomic, strong) Person *person;
@property (nonatomic, strong) Dog *dog;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [Person new];
self.dog = [Dog new];
// 让self.dog 监听 self.person的age 属性的改变
[self.person addObserver:self.dog forKeyPath:@"age" options:0 context:nil];
}
// 点击屏幕改变self.person 的age属性
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.person.age = 10;
}
3.在Dog类的.m文件中调用KVO的监听方法
代码如下:
#import "Dog.h"
@implementation Dog
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
NSLog(@"监听到了%@的%@属性改变了",object, keyPath);
}
这时当我们点击屏幕的时候就会触发监听方法打印内容如下:
以上就是KVO的实现 但是他的底层是怎么实现的呢 ? 接下来我们来了解一下
首先我们再新建一个Person的子类NSKVONotifying Person类
注意:想要监听一个属性的改变就需要调用这个属性的setter方法令他的属性改变,但是在那里调用呢 ? 这时系统就会自动生成一个继承于Person 的类也就是我们所创建的NSKVONotifying Person类,在这个类中会调用Person类中所有发生变化的属性的Setter方法 然后将OC的isa指针指向新创建的class(这个指针告诉oc运行时某个object到底是哪种类型的object
如下:
#import "NSKVONotifyingPerson.h"
@implementation NSKVONotifyingPerson
- (void)setAge:(NSInteger)age {
[super setAge:age];
// 将要改变age属性
[self willChangeValueForKey:@"age"];
// 已经改变了age属性
[self didChangeValueForKey:@"age"];
}
@end
这时我们注意到上面的setter方法中调用了两个方法:
// 将要改变age属性
[self willChangeValueForKey:@"age"];
// 已经改变了age属性
[self didChangeValueForKey:@"age"];
这两个方法的内部就会主动调用Dog类中的监听方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
NSLog(@"监听到了%@的%@属性改变了",object, keyPath);
}
我们可以通过打印Person类的isa 指针来观察他的变化
这时我们需要用到运行时机制并引入两个头文件
#import <objc/runtime.h>
#import <objc/message.h>
我们分别在这两个位置打印Person的isa指针
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [Person new];
NSLog(@"初始化的self.person的类型%@",object_getClass(self.person ));
self.dog = [Dog new];
// 让self.dog 监听 self.person的age 属性的改变
[self.person addObserver:self.dog forKeyPath:@"age" options:0 context:nil];
}
上面的打印结果为:
第二处打印
// 点击屏幕改变self.person 的age属性
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.person.age = 10;
NSLog(@"setter方法赋值是self.person的类型%@",object_getClass(self.person ));
}
打印结果为:
通过观察上面的对比我们就会发现self.person的类型发生了改变
由原来的 Person类 变为NSKVONotifying_Person类