KVO 底层原理详解
基本原理-> 给一个对象的属性添加监听 当属性值发生变化时 会触发监听器的监听的方法
#import "ViewController.h" #import "Person.h" @interface ViewController () @property (nonatomic, strong) Person *p; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.p=[[Person alloc]init]; self.p.age=5; [self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"额外信息"]; // Do any additional setup after loading the view. } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ self.p.age=10; } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ NSLog(@"\n%@\n%@\n%@\n%@",keyPath,object,change,context); /* age <Person: 0x600001390970> { kind = 1; new = 10; old = 5; } 额外信息 */ } -(void)dealloc{ [self.p removeObserver:self forKeyPath:@"age"]; }
2、在给person 添加监听之后、其实苹果底层通过运行时动态给person添加了一个子类 NSKVONotifying_Person
NSLog(@"%s",object_getClassName(self.p)); // Person [self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"额外信息"]; NSLog(@"%s",object_getClassName(self.p)); //NSKVONotifying_Person
3、在新生产的类会新生成4个方法
- (void)viewDidLoad { [super viewDidLoad]; self.p=[[Person alloc]init]; self.p.age=5; [self printMethodNamesOfClass:object_getClass(self.p)]; // Person age, setAge: [self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"额外信息"]; [self printMethodNamesOfClass:object_getClass(self.p)];//NSKVONotifying_Person setAge:, class, dealloc, _isKVOA, } - (void)printMethodNamesOfClass:(Class)cls { unsigned int count; // 获得方法数组 Method *methodList = class_copyMethodList(cls, &count); // 存储方法名 NSMutableString *methodNames = [NSMutableString string]; // 遍历所有的方法 for (int i = 0; i < count; i++) { // 获得方法 Method method = methodList[i]; // 获得方法名 NSString *methodName = NSStringFromSelector(method_getName(method)); // 拼接方法名 [methodNames appendString:methodName]; [methodNames appendString:@", "]; } // 释放 free(methodList); // 打印方法名 NSLog(@"%@ %@", cls, methodNames); }
4个方法介绍
- (void)setAge:(int)age { _NSSetIntValueAndNotify(); } // 屏幕内部实现,隐藏了NSKVONotifying_Person类的存在 - (Class)class { return [MJPerson class]; } - (void)dealloc { // 收尾工作 } - (BOOL)_isKVOA { return YES; }
4、在新生成的这个类中 修改对象的属性时-> 其实会类似重写Set方法- 会调用Foundation 框架中的
_NSSetXXXValueAndNotify 函数:
- willChangeValueForKey:
- 父类原来的setter
- didChangeValueForKey:
- 内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)
5、如要要手动触发一个KVO 监听器的监听方法
- 调用改对象的 willChangeValueForKey
- 调用改对象的 didChangeValueForKey
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ // self.p.age=10; [self.p willChangeValueForKey:@"age"]; [self.p didChangeValueForKey:@"age"]; } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ NSLog(@"\n%@\n%@\n%@\n%@",keyPath,object,change,context); /* age <Person: 0x6000019947f0> { kind = 1; new = 5; old = 5; } 额外信息 */ }
6、修改对象的成员变量是不会触发KVO的、KVC赋值是能够触发KVO