zoukankan      html  css  js  c++  java
  • KVO 底层原理详解

    基本原理-> 给一个对象的属性添加监听 当属性值发生变化时 会触发监听器的监听的方法

    #import "ViewController.h"
    #import "Person.h"
    @interface ViewController ()
    @property (nonatomic, strong) Person *p;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.p=[[Person alloc]init];
        self.p.age=5;
        [self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"额外信息"];
        // Do any additional setup after loading the view.
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        self.p.age=10;
    }
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        NSLog(@"
    %@
    %@
    %@
    %@",keyPath,object,change,context);
        /*
         age
         <Person: 0x600001390970>
         {
             kind = 1;
             new = 10;
             old = 5;
         }
         额外信息
         */
    }
    -(void)dealloc{
        [self.p removeObserver:self forKeyPath:@"age"];
    }

    2、在给person 添加监听之后、其实苹果底层通过运行时动态给person添加了一个子类 NSKVONotifying_Person

       NSLog(@"%s",object_getClassName(self.p)); // Person
        [self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"额外信息"];
        NSLog(@"%s",object_getClassName(self.p)); //NSKVONotifying_Person

    3、在新生产的类会新生成4个方法

    - (void)viewDidLoad {
        [super viewDidLoad];
        self.p=[[Person alloc]init];
        self.p.age=5;
        [self printMethodNamesOfClass:object_getClass(self.p)]; // Person age, setAge:
        [self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"额外信息"];
        [self printMethodNamesOfClass:object_getClass(self.p)];//NSKVONotifying_Person setAge:, class, dealloc, _isKVOA,
    }
    - (void)printMethodNamesOfClass:(Class)cls
    {
        unsigned int count;
        // 获得方法数组
        Method *methodList = class_copyMethodList(cls, &count);
        
        // 存储方法名
        NSMutableString *methodNames = [NSMutableString string];
        
        // 遍历所有的方法
        for (int i = 0; i < count; i++) {
            // 获得方法
            Method method = methodList[i];
            // 获得方法名
            NSString *methodName = NSStringFromSelector(method_getName(method));
            // 拼接方法名
            [methodNames appendString:methodName];
            [methodNames appendString:@", "];
        }
        
        // 释放
        free(methodList);
        
        // 打印方法名
        NSLog(@"%@ %@", cls, methodNames);
    }

    4个方法介绍

    - (void)setAge:(int)age
    {
        _NSSetIntValueAndNotify();
    }
    
    // 屏幕内部实现,隐藏了NSKVONotifying_Person类的存在
    - (Class)class
    {
        return [MJPerson class];
    }
    
    - (void)dealloc
    {
        // 收尾工作
    }
    
    - (BOOL)_isKVOA
    {
        return YES;
    }

    4、在新生成的这个类中 修改对象的属性时-> 其实会类似重写Set方法- 会调用Foundation 框架中的  

    _NSSetXXXValueAndNotify 函数:

    • willChangeValueForKey:
    • 父类原来的setter
    • didChangeValueForKey:
    • 内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)

    5、如要要手动触发一个KVO  监听器的监听方法

    • 调用改对象的  willChangeValueForKey
    • 调用改对象的  didChangeValueForKey
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //    self.p.age=10;
        [self.p willChangeValueForKey:@"age"];
        [self.p didChangeValueForKey:@"age"];
    }
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        NSLog(@"
    %@
    %@
    %@
    %@",keyPath,object,change,context);
        /*
        age
         <Person: 0x6000019947f0>
         {
             kind = 1;
             new = 5;
             old = 5;
         }
         额外信息
         */
    }

     6、修改对象的成员变量是不会触发KVO的、KVC赋值是能够触发KVO

  • 相关阅读:
    程序员要善于在工作中找到偷懒的办法
    关于count(1) 和 count(*)
    前端设计+程序开发那点事
    关于MySQL Connector/C++那点事儿
    windows下编译php5.2.17这是闹哪样?
    easyui使用时出现这个Uncaught TypeError: Cannot read property 'combo' of undefined
    视频文件自动转rtsp流
    Jenkins Pipeline如何动态的并行任务
    Jenkins的Dockerfile中如何批量迁移原Jenkins安装的插件
    Groovy中json的一些操作
  • 原文地址:https://www.cnblogs.com/ZhangShengjie/p/12094434.html
Copyright © 2011-2022 走看看