KVO原理

 

###KVO 常见用法:

 

     1、注册指定key 路径的监听器

 

 - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

 

         observer : 观察者,也就是KVO 通知的订阅者,订阅者需要实现observeValueForKeyPath:ofObject:change: context: 方法;

 

         keyPath  : 描述将要观察的属性,相对于被观察者;

 

         options  : KVO 的一些属性设置
         context  : 上下文
     2、删除指定key 路径的监听器
         - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context;
         - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath

     3、回调监听
         - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
         keyPath:被监听的keyPath , 用来区分不同的KVO监听。
         object: 被观察修改后的对象(可以通过object获得修改后的值)
         change:保存信息改变的字典(可能有旧的值,新的值等)
         context:上下文,用来区分不同的KVO监听
使用步骤:
         1、注册,指定被观察的属性
         2、实现回调方法

 

         3、移除观察

@interface Person : NSObject

@property (nonatomic,assign) NSInteger age;

@end
@interface ViewController ()

@property (nonatomic,strong) Person *p;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.p = [[Person alloc] init];
    //注册监听
    [self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
    self.p.age = 10;
}
//监听回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"age"]) {
        NSLog(@"age is changed = %@",[change valueForKey:NSKeyValueChangeNewKey]);
    }else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:nil];
    }
}
//移除监听
- (void)dealloc {
    [self.p removeObserver:self forKeyPath:@"name" context:nil];
}
}

KVO 基本原理:

     1、KVO 是基于runtime 机制实现的。
     2、当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter方法。派生类在被重写的setter方法内实现真正的通知机制。
     3、如果原类为Person ,那么生成的派生类名为NSKVONotifying_Person
     4、每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷把isa指针指向动态创建的派生类,从而在给被监控属性赋值是执行的是派生类的setter方法。
     5、键值观察通知依赖于NSObject的两个方法:willChangeValueForKey:和 didChangeValueForKey: 在一个被观察属性发生改变之前,willChangeValueForKey:一定会被调用,这就会记录旧的值。当改变发生后,didChangeValueForKey:会被调用,继而observeValueForKey:ofObject:change:context: 也会被调用。

 

   KVO 的实现:

#import "Person.h"
@interface Person (KVO)
- (void)rc_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
@end

@implementation Person (KVO)
- (void)rc_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
    //1、runtime 动态生成Person 类的子类(派生类)
    //2、重写NSKVONotifying_Person 的属性setter方法,目的:监听属性改变
    //3、修改对象的isa 指针
    
    //修改isa 指针
    object_setClass(self, NSClassFromString(@"NSKVONotifying_Person"));
    
    //保存键值观察者对象 self -> p
    //动态添加属性
    objc_setAssociatedObject(self, @"observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

//实现子类(派生类)
#import "Person.h"
@interface NSKVONotifying_Person : Person
@end

@implementation NSKVONotifying_Person
//派生类重写setter 方法
- (void)setAge:(NSInteger)age {
    [super setAge:age];

    //调用观察者的observeValueForKeyPath
    id observer = objc_getAssociatedObject(self, @"observer");
    [observer observeValueForKeyPath:@"age" ofObject:self change:nil context:nil];
}
@end

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.p = [[Person alloc] init];   
    [self.p rc_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
    self.p.age = 10;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"age"]) {
        NSLog(@"age is changed = %@",[change valueForKey:NSKeyValueChangeNewKey]);
    }else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:nil];
    }
}

参考链接:http://tech.glowing.com/cn/implement-kvo/


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值