KVO的本质是什么?

KVO

KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变.

基本应用

在ViewController中监听Person对象的age变化,点击控制器屏幕就改变age值,可以看到收到变化通知.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.p1 = [[Person alloc] init];
    self.p1.age = 10;
    
    [self.p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"age change"];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    self.p1.age = 12;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    NSLog(@"%@",change);
}

点击屏幕可以看到打印

2019-05-07 21:49:11.190686+0800 KVO[38048:2769151] {
    kind = 1;
    new = 12;
    old = 10;
}

KVO探究

添加KVO之前的Person

在添加监听前断点查看Person对象


添加KVO之前

添加KVO之后查看Person对象

添加KVO之后

比对添加KVO添加前后发现,Person对象的类信息发生了改变.p1由Person对象变成了NSKVONotifying_Person对象.

打印下KVO前后方法列表变化

- (NSString *)getMethodList:(Class )cls{
    unsigned int count;
    Method *methods = class_copyMethodList(cls, &count);
    
    NSString *names = @"";
    for (int i = 0 ; i < count; i ++) {
        Method method = methods[i];
        NSString *name = NSStringFromSelector(method_getName(method));
        names = [NSString stringWithFormat:@"%@_%@",names,name];
    }
    return names;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.p1 = [[Person alloc] init];
    self.p1.age = 10;
   
    NSLog(@"KVO之前-->%@",[self getMethodList:object_getClass(self.p1)]);
    
    
    [self.p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"age change"];
    
    NSLog(@"KVO之后-->%@",[self getMethodList:object_getClass(self.p1)]);
}

打印结果:

2019-05-07 22:23:09.775530+0800 KVO[39211:2801239] KVO之前-->_setAge:_age
2019-05-07 22:23:09.775838+0800 KVO[39211:2801239] KVO之后-->_setAge:_class_dealloc__isKVOA

多了三个方法:
1.class:这个也间接证明为啥用实例对象获取class会总是Person
2.dealloc
3.isKVOA

内部实现类似这样:

 (void)setAge:(int)age
{
    _NSSetIntValueAndNotify();
}

// 屏幕内部实现,隐藏了NSKVONotifying_Person类的存在
- (Class)class
{
    return [Person class];
}

- (void)dealloc
{
    // 收尾工作
}

- (BOOL)_isKVOA
{
    return YES;
}

所以对比两个类的结构:


两个类结构对比.png

总结

  • 利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
  • 当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
    1.willChangeValueForKey:
    2.父类原来的setter
    3.didChangeValueForKey:
    内部会触发监听器(Oberser)的监听方法(observeValueForKeyPath:ofObject:change:context:)

你可能感兴趣的:(KVO的本质是什么?)