• Objective-C中的KVO


    Objective-C中的KVO可以用来监听某个属性的变化,当属性发生变化的时候,会通知到观察者。使用KVO,需要在观察者类使用-addObserver:forKeyPath:options:context:接口注册监听,当监听对象有变化的时候,会通过-observeValueForKeyPath:ofObject:change:context:方法将对象的值传递给观察者。

    KVO原理

    一个Object被观察的时候,系统会动态创建Object的子类,以NSKVONotifying开头,在子类中重写属性的set方法,在重写的set方法中调用-willChangeValueForKey:和-didChangeValueForKey:方法,如果一个Object没有观察者的时候,对应的动态子类会被删除。

    新建一个CustomClass类,这个类有属性string,字符串类型

    @interface CustomClass : NSObject
    
    @property(nonatomic, strong) NSString *string;
    
    @end
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在另外一个类中添加KVO监听,并且重写observeValueForKeyPath:ofObject:change:context:方法

    CustomClass *obj = [[CustomClass alloc] init];
    //NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld是枚举,可以同时监听旧值和新值
    [obj addObserver:self forKeyPath:@"string" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    obj.string = @"str";
    
    • 1
    • 2
    • 3
    • 4
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        if ([keyPath isEqualToString:@"string"]) {
            NSString *res = change[NSKeyValueChangeNewKey];
            NSLog(@"%@", res);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在obj.string = @“str”;这行前加断点,断点断住的时候,在lldb调试器中执行 po object_getClass(obj),输出是NSKVONotifying_CustomClass,执行po [obj class],输出是CustomClass。

    obj被监听后,不仅重新了set方法,还重写了class方法,是为了能输出正确的class类型。

    添加移除监听的方法

    [obj removeObserver:self forKeyPath:@"string"];
    
    • 1

    这行代码执行后,再次输入po object_getClass(obj)和po [obj class],输出的都是CustomClass,此时KVO子类已经被释放了。

    KVO可监听的对象

    从KVO的原理可以看出,它的实现基于重写set方法,所以对于成员变量无法通过KVO监听,因为成员变量没有set方法。

    KVO可以监听@property开头的属性变量,因为属性变量有set方法。

    对于.h中声明readonly的对象,如果.m文件中声明为readwrite,这种属性变量也可以监听。

    如果某个属性不想被监听,可以通过重写+automaticallyNotifiesObserversForKey:方法,在方法中返回NO即可。

    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
    {
        if ([key isEqualToString:@"string"]) {
            return NO;
        } else {
            return YES;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    仿射密码 affine
    前端获取图片宽高的几种方法
    string类的常用方法
    ZEMAX | 在OpticStudio中通过几何光线追迹来模拟杨氏双缝干涉实验
    一、ROS2简介
    在 kubernetes 环境下如何优雅扩缩容 Pulsar
    模式识别——贝叶斯决策理论
    专业140+总分400+武汉理工大学855信号与系统考研经验电子信息与通信工程,真题,大纲,参考书
    在线教育项目【课程科目板块】前端vue和后端查询功能实现
    Kryo反序列化链分析
  • 原文地址:https://blog.csdn.net/u011608357/article/details/127952688