KVO 实现原理
KVO即Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。
KVO的原理
- 当一个object有观察者时,动态创建这个object的类的子类
- 对于每个被观察的property,重写其set方法
- 在重写的set方法中调用- willChangeValueForKey:和- didChangeValueForKey:通知观察者
- 当一个property没有观察者时,删除重写的方法
- 当没有observer观察任何一个property时,删除动态创建的子类
当我们在添加观察者的时候,打一个断点,在设置属性的时候打一个断点
发现对象P的isa指针居然变量了,指向的类也不是我们自己创建的,那也就说明是系统自己创建的,Person类的子类NSKVONotifying_Person,并且重写了set方法。其实,就是去判断有没有调用一个对象的set方法
现在我们就手动的模拟下这种情况
首先我们需要创建一个NSObject
的分类,创建自己添加观察者的方法
@interface NSObject (KVO)
- (void)xf_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
@end
#import "NSObject+KVO.h"
#import <objc/runtime.h>
#import "XFKVONotifying_Person.h"
@implementation NSObject (KVO)
- (void)xf_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context {
// 修改isa,改变当前对象的类名
object_setClass(self, [XFKVONotifying_Person class]);
//添加关联,把观察者保存到当前对象里
objc_setAssociatedObject(self, @"observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
由于我们使用了运行时,所以别忘记导入 <objc/runtime.h>
我们还需要创建一个 XFKVONotifying_Person
类,并继承于 Person
#import "Person.h"
@interface XFKVONotifying_Person : Person
@end
#import "XFKVONotifying_Person.h"
#import <objc/runtime.h>
@implementation XFKVONotifying_Person
-(void)setAge:(NSInteger)age {
[super setAge:age];
//取出观察者,并通知观察者属性改变
id observer = objc_getAssociatedObject(self, @"observer");
[observer observeValueForKeyPath:@"age" ofObject:self change:nil context:nil];
}
@end
最后我们调用自己写的方法:
[p xf_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
一样可以观察到属性值的变化
总结
KVO底层实现原理:
- 动态创建NSKVONotifying_Person,NSKVONotifying_Person是Person子类,做KVO
- 修改当前对象的isa指针->NSKVONotifying_Person
- 只要调用对象的set,就会调用NSKVONotifying_Person的set方法
- 重写NSKVONotifying_Person的set方法:
- [super set:]
- 通知观察者,告诉你属性改变
本文作者:
Aeron_Xie
最后更新: 2023年03月25日 22:39:55
本文链接: http://aeronxie.github.io/post/666f6c4d.html
版权声明: 本作品采用 CC BY-NC-SA 4.0 许可协议进行许可,转载请注明出处!
最后更新: 2023年03月25日 22:39:55
本文链接: http://aeronxie.github.io/post/666f6c4d.html
版权声明: 本作品采用 CC BY-NC-SA 4.0 许可协议进行许可,转载请注明出处!