美文网首页
KVO的浅显理解

KVO的浅显理解

作者: FallLeaf | 来源:发表于2018-11-20 23:23 被阅读0次

    近两天看视频教程,粗略的学习了一下KVO的使用。做个笔记,以便后期自己查阅。
    KVO的实质则是通过iOS的runtime来动态的创建监听类的子类,重写子类对象的setter方法。在setter方法中发送通知。

    KVO的使用

    //添加观察者
    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
    //移除观察者
    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context
    

    这两个方法一定是成对出现的。添加后,在dealloc中要将添加的观察者移除。
    简述下添加方法的参数含义:
    1.observer代表的是监听者。
    2.keyPath指的是监听属性的名称。
    3.options有四种

    • NSKeyValueObservingOptionNew 传回新值。
    • NSKeyValueObservingOptionOld 传回旧值。
    • NSKeyValueObservingOptionInitial 注册的时候发送一次通知,监听对象的值发生改变的时候也会发送一次通知。
    • NSKeyValueObservingOptionPrior 监听的对象值发生改变之前发一次,改变之后发一次。

    KVO的手动和自动发送通知方法

    这个方法写在监听类中。在这个方法里我们可以根据监听对象的key值来判定是要返回YES还是NO。默认为YES。

    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
    

    KVO的属性依赖

    例如我们监听一个Person类,Person类里面有个Dog类的对象dog。Dog有age和level两个属性。那现在我们要如何监听dog里面的属性的变化呢?

    首先我们在控制器中添加观察者

    [_person addObserver:self forKeyPath:@"dog" options:NSKeyValueObservingOptionNew context:nil]
    

    在Person类中重写属性依赖的方法

    + (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
        NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
        if ([key isEqualToString:@"dog"]) {
            keyPaths = [[NSSet alloc] initWithObjects:@"_dog.age",@"_dog.level", nil];
        }
        return keyPaths;
    }
    

    这样子我们就完成了对于Person类中dog属性变化的监听。

    KVO的实质及自定义KVO

    KVO的实质

    那么最开始我们讲到,KVO的实质就是通过iOS的runtime来动态创建监听类的子类,然后重写子类的setter方法,这边我们再详细的说明一下。
    首先我们来看一下,Person类在添加KVO前后的isa指针的变化。


    我们在给_person对象添加KVO的地方打了个断点,查看到当前_person的isa指针是指向Person这个类的。

    )
    再来看一下添加_person添加KVO之后的isa指针是指向什么。


    _person添加KVO之后的isa指针指向
    通过两张图我们很明显的看到,_person对象在添加KVO前后的isa指针指向是不同的,这是怎么回事呢?道理很简单,在_person添加KVO的时候,系统就会通过runtime动态的去创建一个Person的子类,也就是NSKVONotifying_Person这个类,也同时的改变的_person的isa指针指向。

    自定义KVO

    这边自己按照视频教程写了一个自定义的KVO。创建一个NSObject的分类,在分类里面自定义一个添加KVO的方法。

    - (void)Leaf_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context {
        //1.自定义NSKVONotifying_Person子类
        NSString *oldClassName = NSStringFromClass(self.class);
        NSString *newClassName = [NSString stringWithFormat:@"LeafKVONotifying_%@",oldClassName];
        
        //创建一个类
        Class myClass = objc_allocateClassPair(self.class, newClassName.UTF8String, 0);
        
        //注册创建的类
        objc_registerClassPair(myClass);
        
        //2.动态的修改调用者的类型
        object_setClass(self, myClass);
        
        //3.添加setName方法 重写父类的setName
        class_addMethod(myClass, @selector(setName:), (IMP)setName, "V@:@");
    }
    
    void setName(id self, SEL _cmd, NSString *nerName) {
        //修改name属性
        //通知外界 willChange didChange
    }
    

    最终重写setter的方法里面没有完全的实现。后续会补充完整。这里面就是利用消息机制给这个方法发送通知,这样我们就能在这个方法里面收到属性变化后的通知。

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    

    总结

    以上就是我自己对于KVO学习之后的一些理解。后续还有一些关于KVO对于容器类监听的理解,会慢慢补充。还有很多不足之处,一起学习,共勉。

    相关文章

      网友评论

          本文标题:KVO的浅显理解

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