zoukankan      html  css  js  c++  java
  • OC中的KVO原理

    KVO

    KVO的全称是Key-Value Observing,俗称"键值监听",一般用于监听某个对象属性值的改变

    KVO代码实现

    #import "ViewController.h"
    #import <objc/runtime.h>
    
    @interface LBPerson : NSObject
    @property (nonatomic, assign) int age;
    @end
    
    @implementation LBPerson
    
    
    
    @end
    @interface ViewController ()
    @property (nonatomic, strong) LBPerson *person;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        LBPerson *person = [[LBPerson alloc] init];
        self.person = person;
        [person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"test"];
        
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        self.person.age = 10;
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
    }
    
    - (void)dealloc {
        [self.person removeObserver:self forKeyPath:@"age"];
    }
    @end

    打印结果如下:

    2020-11-23 22:14:22.171102+0800 KVOTest[60482:2523973] 监听到<LBPerson: 0x600000ccc670>的age属性值改变了 - {
        kind = 1;
        new = 10;
        old = 0;
    } - test

    对比对对象的属性添加监听后的打印变化

    新增如下代码:

    - (void)viewDidLoad {
        [super viewDidLoad];
        LBPerson *person = [[LBPerson alloc] init];
        self.person = person;
        LBPerson *person1 = [[LBPerson alloc] init];
        NSLog(@"Person类的对象添加监听之后- %@ %@", object_getClass(self.person), object_getClass(person1));
        NSLog(@"Person类的对象添加监听之后- %p %p", [self.person methodForSelector:@selector(setAge:)], [person1 methodForSelector:@selector(setAge:)]);
        NSLog(@"元类对象--- %@ %@",object_getClass(object_getClass(self.person)),object_getClass(object_getClass(person1)));
        [person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"test"];
        
        NSLog(@"Person类的对象添加监听之后- %@ %@", object_getClass(self.person), object_getClass(person1));
        NSLog(@"Person类的对象添加监听之后- %p %p", [self.person methodForSelector:@selector(setAge:)], [person1 methodForSelector:@selector(setAge:)]);
        NSLog(@"元类对象--- %@ %@",object_getClass(object_getClass(self.person)),object_getClass(object_getClass(person1)));
    }

    打印结果如下:

    2020-11-23 22:18:47.625352+0800 KVOTest[60504:2526933] Person类的对象添加监听之后- LBPerson LBPerson
    2020-11-23 22:18:47.625659+0800 KVOTest[60504:2526933] Person类的对象添加监听之后- 0x1075a9670 0x1075a9670
    2020-11-23 22:18:47.625850+0800 KVOTest[60504:2526933] 元类对象--- LBPerson LBPerson
    2020-11-23 22:18:47.626450+0800 KVOTest[60504:2526933] Person类的对象添加监听之后- NSKVONotifying_LBPerson LBPerson
    2020-11-23 22:18:47.626652+0800 KVOTest[60504:2526933] Person类的对象添加监听之后- 0x7fff207d2ce3 0x1075a9670
    2020-11-23 22:18:47.626801+0800 KVOTest[60504:2526933] 元类对象--- NSKVONotifying_LBPerson LBPerson

    对比打印结果,对象的某属性被监听之后,系统自动帮忙创建了NSKVONotifying_类名的一个新类,对于两个对象的setAge的打印结果同样可以得到证明,添加监听之前

    打印setAge的方法地址是相同的,监听之后,地址不太相同。

    下面我在LBPerson类的下面。创建一个继承于LBPerson的NSKVONotifying_LBPerson,代码如下

    @interface LBPerson : NSObject
    @property (nonatomic, assign) int age;
    @end
    
    @implementation LBPerson
    
    @end
    
    @interface NSKVONotifying_LBPerson : LBPerson
    
    @end
    
    @implementation NSKVONotifying_LBPerson
    
    
    @end

    再运行之后打印结果如下:

    2020-11-23 22:27:39.146253+0800 KVOTest[60558:2532017] [general] KVO failed to allocate class pair for name NSKVONotifying_LBPerson, automatic key-value observing will not work for this class

    也可以正式再添加kvo之后,通过runtime创建了一个分类

    监听属性的方法调用顺序

    在LBPerson类中添加重写如下方法,添加这些方法的目的是让系统生成的中间类能够继承这些方法,打印出我们想要的数据:

    @interface LBPerson : NSObject
    @property (nonatomic, assign) int age;
    @end
    
    @implementation LBPerson
    - (void)setAge:(int)age {
        _age = age;
        NSLog(@"setAge:");
    }
    
    - (void)willChangeValueForKey:(NSString *)key {
        [super willChangeValueForKey:key];
        NSLog(@"willChangeValueForKey");
    }
    
    - (void)didChangeValueForKey:(NSString *)key {
        NSLog(@"didChangeValueForKey Start");
        [super didChangeValueForKey:key];
        NSLog(@"didChangeValueForKey End");
    }
    @end

    打印结果如下:

    2020-11-23 22:35:27.686978+0800 KVOTest[60592:2536626] willChangeValueForKey
    2020-11-23 22:35:27.687163+0800 KVOTest[60592:2536626] setAge:
    2020-11-23 22:35:27.687345+0800 KVOTest[60592:2536626] didChangeValueForKey Start
    2020-11-23 22:35:27.687813+0800 KVOTest[60592:2536626] 监听到<LBPerson: 0x6000007cc4e0>的age属性值改变了 - {
        kind = 1;
        new = 10;
        old = 0;
    } - test
    2020-11-23 22:35:27.688023+0800 KVOTest[60592:2536626] didChangeValueForKey End

    从上述打印得到的方法调用顺序:

    willChangeValueForKey
    setAge:
    didChangeValueForKey
    observeValueForKeyPath
  • 相关阅读:
    【YBTOJ】守卫挑战
    【YBTOJ】【Luogu P6089】[JSOI2015]非诚勿扰
    【Luogu P4092】[HEOI2016/TJOI2016]树
    【YBTOJ】【Luogu P3232】[HNOI2013]游走
    【CodeForces 396B】On Sum of Fractions
    【P2579】【ZJTSC05】沼泽鳄鱼
    【YBTOJ】【USACO03MAR】最大均值
    【YBTOJ】防具布置
    VC 静态库与动态库(二)静态库创建与使用
    VC 静态库与动态库(一)介绍
  • 原文地址:https://www.cnblogs.com/muzichenyu/p/14027649.html
Copyright © 2011-2022 走看看