首先实现系统KVO
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_p = [[Person alloc]init];
[_p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"%@",_p);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
_p.name = @"xxx";
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"%@",change);
}
- (void)dealloc
{
[_p removeObserver:self forKeyPath:@"name"];
} ```
#### addObserver:forKeyPath:options:context:各个参数的作用分别是什么
/**
- self.person:要监听的对象
- 参数说明:
- @param addObserver 观察者,负责处理监听事件的对象
- @param forKeyPath 要监听的属性
- @param options 观察的选项(观察新、旧值,也可以都观察)
- @param context 上下文,用于传递数据,可以利用上下文区分不同的监听
*/
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];
/**
- 当监控的某个属性的值改变了就会调用
- @param keyPath 监听的属性名
- @param object 属性所属的对象
- @param change 属性的修改情况(属性原来的值
oldValue
、属性最新的值newValue
) - @param context 传递的上下文数据,与监听的时候传递的一致,可以利用上下文区分不同的监听
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@对象的%@属性改变了:%@", object, keyPath, change);
} ```
KVO内部实现原理
- KVO是基于runtime机制实现的
当某个类的属性对象第一次
被观察时,系统就会在运行期动态
地创建该类的一个派生类
,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
- 如果原类为Person,那么生成的派生类名为
NSKVONotifying_Person
- 每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法
- 键值观察通知依赖于NSObject 的两个方法:
willChangeValueForKey:
和didChangevlueForKey:
;在一个被观察属性发生改变之前,willChangeValueForKey:
一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而observeValueForKey:ofObject:change:context:
也会被调用。
补充:KVO的这套实现机制中苹果还偷偷重写了class方法,让我们误认为还是使用的当前类,从而达到隐藏生成的派生类
自己实现KVO
- 创建NSKVONotifying_Person 继承于Person,重写属性set方法
#import "NSKVONotifying_Person.h"
#import <objc/runtime.h>
@implementation NSKVONotifying_Person
- (void)setName:(NSString *)name
{
[super setName:name];
id observer = objc_getAssociatedObject(self, @"observer");
[observer observeValueForKeyPath:@"name" ofObject:self change:nil context:nil];
}
@end ```
- 创建一个类别 NSObject (KVO),实现自定义的添加监听的方法
import "NSObject+KVO.h"
import <objc/runtime.h>
@implementation NSObject (KVO)
-
(void)Ec_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void )context
{
/
1.runtime动态生成Person的子类
2.重写KVO_Person的属性set方法 目的:监听属性有没有变化
3.修改对象的isa指针
*///修改isa
object_setClass(self, NSClassFromString(@"NSKVONotifying_Person"));//保存观察者对象
objc_setAssociatedObject(self, @"observer", observer, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
@end ```
- 注意:在NSObject (KVO)中要保存观察者对象,这样可以在NSKVONotifying_Person类里获取观察者对象,然后调用回执方法。
网友评论