KVO

作者: 越天高 | 来源:发表于2020-10-20 22:11 被阅读0次

    基本使用

    • iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)
      KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        Person *p = [[Person alloc] init];
        _p = p;
        p.age = 3;
        [_p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
        
        
    }
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        _p.age = 5;
    }
    
    //实现监听方法
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
         NSLog(@"监听到%@的属性%@改变了%@", object, keyPath,change);
    }
    
    -(void)dealloc
    {
        [_p removeObserver:self forKeyPath:@"age"];
    }
    

    02 存在的疑问

    在我们看来两个person的实例对象都是在调用setAge的方法,但是其中一个可以调用kvo的方法,另一个却不可以

    03本质分析

    我们通过打印两个实例对象的isa发现,他们的类对象并不是一个,如果实现了kvo他的类对象就不是person类对象,而是NSKVONotifying_Person,这个类是runtime动态生成类,他算是Person的一个子类,


    l连个实例的不同父类

    如果没有实现KVO我们一调用方法,他就会根据isa找到Person的类对象,然后再找到setAge进行调用,
    实现了KVO之后,他会先isa找个类对象NSKVONotifying_Person,找到这个里面setAge,这个里面的setAge会调用foundation里面一个函数——NSSetIntValueAndNotify
    这个函数大概干了

    willChangeValueForKey;
    [super setAge];
    didChangeValueForKey
    
    didChangeValueForKey
    {
    //通知坚听者 属性改变
    }
    
    

    调用willChangeValueForKey:

    调用原来的setter实现
    
    调用didChangeValueForKey:
    didChangeValueForKey:内部会调用observer的observeValueForKeyPath:ofObject:change:context:
    

    这就是为什么两种不同结果

    04本质验证

    KVO没有代理的性能高,因为他还要创建新的类,代理直接找到代理执行方法
    根据methodforselect获取方法的实现,可以看出来在添加KVO监听之后,他们的地址就不一样了
    可以进入lldb调试来看一下该方法的具体实现
    po (IMP)地址 可以看出来他调用的是foundation里面一个函数——NSSetIntValueAndNotify


    截屏2020-10-20下午8.38.28.png

    NSKVONotifying_Person他的isa也是只想自己的元类对象,

    • 未使用KVO监听对象


      未使用KVO监听对象
    • 使用了KVO监听对象


      使用了KVO监听对象

    05窥探foundation

    foundation就是系统给我默认添加框架,但是只有头文件,我们是看不见的。他.m文件都在framework里面keyi

    6内部调用流程

    我们虽然不能从在NSKVONotifying_Person里面重写willChangeValueForKey来查看他是否和我们预想的一样,但是我们可以通过更改他的父类来查看

    @implementation Person
    
    - (void)setAge:(int)age
    {
        _age = age;
        NSLog(@"setAge:");
    
    }
    - (void)willChangeValueForKey:(NSString *)key
    {
        NSLog(@"willChangeValueForKey");
        [super willChangeValueForKey:key];
    
    }
    - (void)didChangeValueForKey:(NSString *)key
    {
        NSLog(@"didChangeValueForKeyBegin");
        [super didChangeValueForKey:key];
        NSLog(@"didChangeValueForKeyEnd");
    }
    

    打印的结果和我们预期是一致的说明我们的判断是正确的


    KVO的方法调用

    07字类内部的方法

    NSKVONotifying_Person这个动态生成的子类除了会重写setAge之外,还是会重写class dealloc isKVOA方法
    isKVOA 返回YES
    dealloc 做一些收尾工作
    class 他重写这个方法是为了让他返回的类对象还是Person类对象 ,用runtime来返回还是真正的对象NSKVONotifying_Person。屏蔽内部实现,隐藏的这个类的存在,如果不重写的话,就会返回这个类的真正的类型

    08子类内部的方法2

    可以利用runtime的方法来打印这个类的所有方法

    -(void)printMethodNameFromClass:(Class)cls
    {
        unsigned count;
        //获得方法数组
        Method *methodList = class_copyMethodList(cls, &count);
        
        //储存方法名
        NSMutableString *methodNames = [NSMutableString string];
        
        for (int i = 0; i < count; i++)
        {
            Method *m = methodList[i];
            //获得方法名字
            NSString *name = NSStringFromSelector(method_getName(m));
            [methodNames appendString:name];
            [methodNames appendString:@" "];
            
        }
        NSLog(@"%@", methodNames);
    
    }
    

    可以查看到他的真实类对象却是包含了


    真实的方法名

    09答疑问

    iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)
    利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
    当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
    willChangeValueForKey:
    父类原来的setter
    didChangeValueForKey:
    内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)

    如何手动出发KVO
    手动调用willChangeValueForKey:和didChangeValueForKey:

    直接修改成员变量会触发KVO么?
    _p1->_age = 5;
    不会,因为他没有执行setAge方法

    相关文章

      网友评论

        本文标题:KVO

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