美文网首页
KVO的本质是什么?

KVO的本质是什么?

作者: 程序猿_K | 来源:发表于2019-05-07 22:41 被阅读0次

    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<UITouch *> *)touches withEvent:(UIEvent *)event{
        self.p1.age = 12;
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)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的本质是什么?

          本文链接:https://www.haomeiwen.com/subject/skesoqtx.html