1、KVO简单介绍
KVO(Key Value Observing)键值监听,属于苹果官方API。
作用:iOS开发中通常将代码分为数据模型和试图控件,数据模型维护程序的状态数据,试图控件将数据模型中的数据展示出来。而KVO能监听当数据模型发生改变时,及时通知试图控件动态将改变的数据显示出来。
注意:只有通过setter方法改变的属性值,才能被KVO监听到,才能触发observeValueForKeyPath:ofObject:change:context:回调方法
KVO的使用:
1.注册需要观察的对象的属性addObserver:forKeyPath:options:context:
2.实现observeValueForKeyPath:ofObject:change:context:方法,这个方法当观察的属性变化时会自动调用
3.取消注册观察removeObserver:forKeyPath:context:
- (void)viewDidLoad {
[super viewDidLoad];
self.stu = [[Student alloc] init];
// 添加观察者
// observer:观察者 keyPath:想要监听的属性名 options:枚举 回调时可以获取改变前后的值 content:上下文
[self.stu addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}
// 属性发生改变时的回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
// keyPath:属性名 change:改变前后的属性值 content:上下文
NSLog(@"%@", change[@"new"]);
}
// 释放观察者
- (void)dealloc
{
[self.stu removeObserver:self forKeyPath:@"age"];
}
2、KVO底层实现(记录自小马哥视频)
当KVO被触发时
1>首先系统会自动创建NSKVONotifying_Student类(这里以Student为例), NSKVONotifying_Student是Student的子类
2>修改当前对象(student)的isa指针,指向NSKVONotifying_Student
3>只要调用对象的setter方法,就会调用NSKVONotifying_Student的setter方法
4>重写NSKVONotifying_Student的setter方法:[super set:],通知观察者对象的属性值发生改变
下面我们来自己实现KVO,以便更好的理解KVO的底层实现过程。
1>系统KVO是基于NSObject,所以我们可以创建一个NSObject的分类NSObject+KVO
NSObject+KVO.h文件
#import <Foundation/Foundation.h>
@interface NSObject (KVO)
- (void)zhaoName_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
@end
NSObject+KVO.m文件
#import "NSObject+KVO.h"
#import <objc/runtime.h>
#import "ZKVONotifying_Student.h"
@implementation NSObject (KVO)
- (void)zhaoName_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
// 修改isa指针 本质是改变当前对象的类名
object_setClass(self, [ZKVONotifying_Student class]);
// 保存观察者 即动态添加属性
// object:给哪个对象添加属性 key:属性名 value:属性值 policy:关联策略
objc_setAssociatedObject(self, @"observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
2>重写子类NSKVONotifying_Student的setter方法,通知观察者
NSKVONotifying_Student.m文件
#import "ZKVONotifying_Student.h"
#import <objc/runtime.h>
@implementation ZKVONotifying_Student
- (void)setAge:(NSUInteger)age
{
[super setAge:age];
// 获取保存的观察者
id observer = objc_getAssociatedObject(self, @"observer");
// 通知
// 注意这里change传nil,则在回调时change为空
[observer observeValueForKeyPath:@"age" ofObject:self change:nil context:nil];
}
@end
这样调用我们自己实现的KVO也能实现属性改变的监听
[self.stu zhaoName_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];