KVO监听 是通过对对象属性setter的监听实现
- 首先我们写个KVO 简单栗子🌰
- 写一个Dog类 拥有一个age属性
#import <Foundation/Foundation.h>
@interface Dog : NSObject
// 年龄
@property (nonatomic, assign) int age;
@end
- 在控制器中监听 Dog的age属性
#import "ViewController.h"
#import "Dog.h"
@interface ViewController ()
@property (nonatomic, strong) Dog *dog;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Dog *dog = [[Dog alloc] init];
_dog = dog;
// 监听dog的age属性
[dog addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
NSLog(@"age is %d", _dog.age);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
_dog.age++;
}
@end
此时运行程序 点击屏幕输出
此时 age值一改变 我们在监听方法中立马能知道值的变化
为了验证 KVO是通过 属性的setter方法去监听
修改下方法
直接访问成员变量
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// _dog.age++;
_dog-> _age++;
}
此时会报错 因为 @property
会生成私有的成员变量 将其声明为public
@interface Dog : NSObject {
@public
int _age;
}
//年龄
@property (nonatomic, assign) int age;
@end
此时再运行 点击屏幕 发现没有输出了
为验证是否执行了 监听方法 在监听方法里打上断点
说明 监听方法 确实执行了
从而也证明了 KVO底层是对对象属性setter 的监听
============================
继续研究KVO的原理
- 在添加观察者之前 打上断点 观察此时
dog
的isa
指针
此时dog
的isa
指针指向它的父类 Dog
单步执行 添加完 监听者以后 再观察isa
指针
此时 `isa` 指针已经不再指向`Dog` 指向`NSKVONotifying_Dog`
调用 `dog`属性的`setter` 将不再去类`Dog` 中去找了 而是去类`NSKVONotifying_Dog`中找
在`NSKVONotifying_Dog`类中 重写`setter`通知观察者值的改变
修改`isa` 本质是就是修改当前对象的类名
`Class object_setClass(id object, Class cls)`