美文网首页
笔记(二):KVO

笔记(二):KVO

作者: RBNote | 来源:发表于2018-07-22 16:05 被阅读12次
    你以为的并不是你以为的
    
    1-1KVO原理.png

    一 基本使用

    KVO: key-value observing

    //Person 
    @interface Person : NSObject
    @property (nonatomic, assign) int age;
    @end
    
    implementation Person
    @end
    
    //VC
    #import "ViewController.h"
    #import "Person.h"
    
    @interface ViewController ()
    @property (nonatomic, strong) Person *person1;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        
        self.person1 = [[Person alloc] init];
        self.person1.age = 1;
       
        NSKeyValueObservingOptions options = NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
        
        [self.person1 addObserver:self forKeyPath:@"age" options:options context:nil];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
        self.person2.age = 20;
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        NSLog(@"监听到:%@的%@:属性的值发生了改变:%@",keyPath,object,change);
    }
    
    
    //移除监听。
    - (void)dealloc {
        [self.person1 removeObserver:self forKeyPath:@"age"];
    }
    

    二 KVO本质

    利用Runtime动态生成一个子类 NSKVONotify_xxxxxx
    重写子类的setter方法。
    

    从两个方法可以证明对一个对象添加KVO监听之后,KVO内部实现会创建一个新的子类NSKVONotify_xxxxx

    eg1:自己创建一个继承自Person的子类  NSKVONotify_Person 什么不做。运行程序。控制他打印如下信息:KVO不起作用了。
    KVO failed to allocate class pair for name NSKVONotifying_Person, automatic key-value observing will not work for this class
    
    1-2-KVONotWorkForThisClass.png
    eg2:打印一下实例对象的isa指针
    //KVO监听的实例对象的isa指针
    p self.person1.isa 
    (Class) $0 = NSKVONotifying_Person
    
    //未进行KVO监听的实例对象的isa指针 
    p self.person2->isa
    (Class) $7 = Person
    
    1-3添加KVO监听之后的isa.png

    2-1猜测KVO内部实现

    苹果不开源,只能靠猜。

    kvo 实现原理,给Person对象添加KVO监听为例
    1.利用runtime函数动态创建一个继承制Person的子类NSKVONotifying_Person。
    2.重写setter方法。(NSKVONotifying_Person 重写了父类Person的setAag:方法 ,
    在setter方法内部调用Foundation框架的 _NSSetIntValueAndNotify() 这个函数
    3.这个函数 内部做了以下操作
    3-1  调用 willChangeValueForKey:方法 ✅ 
    3-2 调用父类的setter方法。
    3-3 调用 didChangeValueForKey: 方法  ✅
    3-3-1 该方法内部调用了 observeValueForKeyPath: ofObject: change: context: 通知监听器,xxxx属性值发生了改变。
    
    //伪代码 
    @implementation NSKVONotifying_Person
    - (void)setAge:(int)age {
        _NSSetIntValueAndNotify();
    }
    
    //伪代码
    void _NSSetIntValueAndNotify()
    {
        [self willChangeValueForKey:@"age"];
        [super setAge:age];
        [self didChangeValueForKey:@"age"];
    }
    - (void)didChnageValueForKey:(NSString *)key {
    
        //通知监听器,某某属性值发生了改变。
        [observer observeValueForKeyPath:key ofObject:self change:nil context:nil];
    }
    @end
    

    用图形来描述就是:


    1-4NSKVONotifying的内部实现.png

    关于 _NSSetIntValueAndNotify() 函数,可以使用runtime提供的函数 && LLDB指令来查看。


    1-5关于_NSSetIntValueAndNotify.png

    三 题

    1.kvo 的实现原理?(本质?)

    1.使用runtime动态生成一个子类 NSKVONotifying_xxxxx ,并让instance对象的isa指向这个全新的子类。
    2.该子类重写了父类的setter方法,当修改instance对象的属性时,会调用Foundation框架的 _NSSetXXXXValueAndNotify() 函数,该函数内部做了以下操作
    2-1.调用 willChangeValueForKey:
    2-2 调用父类的 setter方法
    2-3 调用 didChangeValueForKey: ✅ 
    2-3-1:didChangeValueForKey  ✅方法内部会触发监听器的(observer) 监听方法( observeValueForKeyPath:ofObject:change:context)
    

    2.手动触发KVO?

    手动调用 这两个方法
    willChangeValueForKey: ✅
    didChangeValueForKey: ✅
    

    3.修改成员变量会触发KVO?

    NO
    

    示例代码 LowLayerTheoryNote

    相关文章

      网友评论

          本文标题:笔记(二):KVO

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