KVO的底层实现原理

2017-02-14  本文已影响79人  6ffd6634d577
什么是KVO ?

KVO即Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。
简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。

KVO的使用比较简单,基本上都是三步:

  1. 注册观察者
    addObserver:forKeyPath:options:context:
  1. 观察者中实现
    observeValueForKeyPath:ofObject:change:context:
  2. 移除观察者
    removeObserver:forKeyPath:

接下来我们通过一个小Demo来探索KVO的底层实现:

1.新建工程后创建一个Person类

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property(nonatomic,copy)NSString *name;

@end
-----------------------------------------
#import "Person.h"

@implementation Person

@end

控制器里的代码如下:

#import "ViewController.h"
#import "Person.h"

@interface ViewController ()

@property(nonatomic,strong)Person *p;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person * p =[[Person alloc] init];
    self.p = p;
    p.name = @"张三";
    p.height = 170;
    
    //添加KVO 监控 p 当中 属性height 的改变
    [p addObserver:self forKeyPath:@"height" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    
    p.height = 180;
}

//监控属性变化触发的方法..
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"height"]) {
        
        NSLog(@"%@",change);
        
    }
}


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

第一次在添加监听者的时候打断点p对象的isa指针如下:

4580E4B2-32C8-40C5-9B9F-CFAE21F9173F.png

再往下走一步的时候p的isa指针如下:

A30651A7-4950-4BE8-B0C3-A8D04FB82E75.png

再往下走的时候就会出发KVO的监听方法

B1F8ECED-78D1-491C-B3A4-D1F69740EFC8.png

接下来我们用setValue:forKey(KVC)方法给p的属性赋值:

//    p.height = 180;
    [p setValue:@(180) forKey:@"height"];

控制台打印结果如下:

40F329D8-BA1B-4116-9AC6-0700E4F655E6.png

接下来我们测试一下直接给p的属性赋值,不通过set方法:

在Person类中添加一个方法:
-(void)changeHeight
{
    _height = 200;
}

在控制器类中:
//    p.height = 180;
//    [p setValue:@(180) forKey:@"height"];
    [p changeHeight];

控制台没有打印任何信息,说明没有触发KVO的监听方法:

C60729A0-0111-46CA-BA73-E00C1A140751.png
总结:

可见,在addObserver:forKeyPath:options:context:之后,对象p的isa变为了NSKVONotifying_Person。
所以,根据上面的isa介绍,响应setAge的方法,实际上是对象p的isa即NSKVONotifying_Person类的setAge方法,并不是原Person类的setAge方法。
可以大胆想象,NSKVONotifying_Person类是Person类的子类,在NSKVONotifying_Person类内部,重写了setAge方法,并且在setAge方法里让监听器调用了observeValueForKeyPath:ofObject:change:context:方法。
那么,NSKVONotifying_Person是怎么产生的呢?简单的说,是由runtime产生的。

上一篇 下一篇

猜你喜欢

热点阅读