iOS-KVO 实现原理

来源:互联网 发布:ubuntu下安装pyqt4 编辑:程序博客网 时间:2024/04/29 20:21

KVO 属性观察者 是观察者模式的一种具体实现,主要通过观察对象属性值的变化,触发对应的方法

下面学习下 KVO 的具体实现 和 底层实现原理


原生观察者实现

1、为 狗 对象 d 添加一个观察者为 自己, 同时监听对象 d->age 年龄 属性的改变

Dog *d = [Dog new];[p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];

2、实现观察者监听方法,实时监听 d->age 年龄 属性的改变,并获取改变后的 new

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {    NSLog(@"%@-%@", keyPath,change[@"new"]);}

3、在对象销毁前,移除狗对象的观察者

- (void)dealloc {    [self.d removeObserver:self forKeyPath:@"age"];}

系统 KVO 的底层实现原理

  • KVO 是基于 runtime 机制实现的
  • 首先是给 NSObject 添加一个分类 (Category), 添加两个方法:
    • 方法一:为自己添加观察者(observer)
    • 方法二:属性变化后执行的动作(action)
  • 为这个类的对象添加观察者(方法一)和实现属性变化后的动作方法(方法二)
  • 当某个类的对象第一次被观察时(方法一),系统就会在运行时动态地创建该类的一个派生类
  • 在这个类中实现添加观察者方法(方法一),并将观察者对象和自己关联起来,并将指针指向生成的派生类
  • 在这个派生类中重写基类中被观察属性的 setter 方法,并获取对象的关联对象
  • 然后在 setter 方法中,执行 关联对象 的方法二,触发对象变化后的 action 动作,这样属性变化后的方法将会执行,达到监听的作用

具体代码实现

一、给 NSObject 添加一个分类 (Category), 添加两个方法,并空实现
@interface NSObject (KVO)- (void)xm_addObserver:( NSObject * _Nonnull )observer forKeyPath:(NSString * _Nonnull)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;- (void)xm_observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;@end
@implementation NSObject (KVO)#pragma mark - 空实现- (void)xm_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {}- (void)xm_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {}@end

二、为某个类的对象添加观察者(方法一)和实现属性变化后的动作方法(方法二)
Dog *d = [Dog new];[d xm_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
- (void)xm_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {    NSLog(@"%@------%@", keyPath, change[@"new"]);}

三、当某个类的对象第一次被观察时(方法一),在运行时动态地创建该类的一个派生类
@interface KV0_Dog : Dog@end

四、在这个类中实现添加观察者方法(方法一),并将观察者对象和自己关联起来,并将指针指向生成的派生类
#import "Dog.h"#import <objc/message.h>@implementation Dog- (void)xm_addObserver:( NSObject * _Nonnull )observer forKeyPath:(NSString * _Nonnull)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context {    // 修改对象的 ias 指针    object_setClass(self, NSClassFromString(@"KV0_Dog"));    // 关联对象,将观察者对象和自己关联起来    objc_setAssociatedObject(self, "observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}@end

五、在这个派生类中重写基类中被观察属性的 setter 方法,并获取对象的关联对象,执行关联对象 的方法二,触发对象变化后的 action
#import "KV0_Dog.h"#import <objc/message.h>#import "NSObject+KVO.h"@implementation KV0_Dog- (void)setAge:(NSInteger)age {    // 重写父类的属性 setter 方法    [super setAge:age];    // 获取自己(父类)的关联对象,观察者对象    id observer = objc_getAssociatedObject(self, "observer");    // 触发观察者对象的 observeValueForKeyPath 方法, 并传递参数    [observer xm_observeValueForKeyPath:@"age" ofObject:self change:@{@"kind": @(1), @"new": @(age)} context:nil];}@end

六、改变 Dogage 属性,然后 步骤二的属性观察方法将会被执行,同时属性改变的新值也传递过来了

打印结果

age------1age------2age------3age------4...
原创粉丝点击