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

 

posted @ 2020-11-23 22:40  木子沉雨  阅读(228)  评论(0编辑  收藏  举报