KVO的底层实现

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值