关于KVO分析总结笔记

一.KVO基础

KVO的全称是Key-Value Observing,俗称键值监听,可以用于监听某个对象属性值的改变
通过
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
方法给类的某个对象添加监听。

在监听类中实现
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context
进行监听

observer:监听方
keyPath:监听对象
optionsNSKeyValueObservingOptionNew(提供更改前的值) NSKeyValueObservingOptionOld(提供更改后的值)
NSKeyValueObservingOptionInitial (观察最初的值,再注册观察服务时会调用一次触发方法)
NSKeyValueObservingOptionPrior(分别在值修改前后触发方法,即一次修改有两次触发)
context:传入的内容

示例:

self.person1 = [[GYPerson alloc] init];
self.person1.age = 10;
[self.person1 addObserver:self
                  forKeyPath:@"age"
                     options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
                     context:@"123"];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"%@ - %@ - %@ - %@", keyPath, object, change, context);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.person1.age = 100;
}

运行结果:


5345344-0919eb72b8f81d23.png
1.png

二.KVO剖析

我们重新定义一个GYPerson的对象person2,不对person2中的任何属性进行监听。分别打印person1person2isa指针来观察他们的类

5345344-2d2d165602585aa3.png
2.png

可以观察到他们的所属的类已经不一样的,添加监听的 person1属于 NSKVONotifying_GYPerson类,未添加监听的 person2仍属于 GYPerson类。
或者
我们使用 RunTimeobject_getClass方法查看

NSLog(@"person1的Class%@", object_getClass(self.person1));
NSLog(@"person2的Class%@", object_getClass(self.person2));

如图:


5345344-c7ef947615ae7d94.png
3.png

同样说明刚刚的验证

原因:在对一个类的对象添加监听后,系统会利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类。全新的子类中重新实现了setAge:方法,setAge:方法实现了foundation中的_NSSetIntValueAndNotify方法

验证:

1.重写setAge:方法验证:

我们方别打印方法地址

NSLog(@"person1的setAge:方法地址 %p", [self.person1 methodForSelector:@selector(setAge:)]);
NSLog(@"person2的setAge:方法地址 %p", [self.person2 methodForSelector:@selector(setAge:)]);

可以观察到:person2setAge:方法在GYPerson中,而person1setAge:方法却在foundation中的_NSSetLongLongValueAndNotify中。

5345344-e28d050e7633383d.png
4.png

2.对生成的子类NSKVONotifying_GYPerson类中setAge:方法猜测
5345344-58a262488028443d.png
5.png
3.验证_NSSetIntValueAndNotify中的调用流程:

我们可以在GYPerson中实现重写 willChangeValueForKey:didChangeValueForKey:方法,打印log,观察调用顺序。(didChangeValueForKey:内部会调用observer的observeValueForKeyPath:ofObject:change:context:方法 )

5345344-d32ef3bd80b8b854.png
6.png

结果
5345344-038b3528d42459fa.png
7.png

Tips

1.在NSKVONotifying_GYPerson类中,重写了以下方法,从而让开发者不会产生过多疑惑。改新生成子类中包含一些其他方法,可以通过RunTimeclass_copyMethodList及其他函数查看或验证。

// 内部实现,隐藏了NSKVONotifying_MJPerson类的存在 [self.person class]获取出的类名都是GYPerson
- (Class)class {
    return [GYPerson class];
}

2.在工程中手动创建NSKVONotifying_GYPerson类,会导致addObserver失败

3.手动触发KVO的方法,其实就是手动调用willChangeValueForKeydidChangeValueForKey方法,但是这两个方法需要成对出现,尽管触发observeValueForKeyPath的操作是在didChangeValueForKey中,可以理解为父类中添加了相关的判断。

猜你喜欢

转载自blog.csdn.net/weixin_34184158/article/details/86989691
KVO
今日推荐