KVO的底层实现

来源:互联网 发布:aws windows 2012 编辑:程序博客网 时间:2024/06/05 08:32

了解一下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类

0 0