KVO总结

基本用法

#import "ViewController.h"
#import "KVOObject.h"
@interface ViewController ()
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    KVOObject * obj1 = [[KVOObject alloc] init];
    //添加监听
    [obj1 addObserver:self forKeyPath:@"identifier" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionNew  context:NULL];
    KVOObject * obj2 = [[KVOObject alloc] init];
    //赋值
    obj1.identifier = @"obj1";
    obj2.identifier = @"obj2";   
}

//监听回调
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"identifier"]) {        
        NSLog(@"%@",change);
    }
}
@end

原理

  • 查看object的isa,如图:

20180809203210

发现object对象的isa指向NSKVONotifying_KVOObject,由此可知,
当调用-[KVOObject addObserver:forKeyPath:options:context:];时,runtime动态添加了NSKVONotifying_KVOObject类,并且让object的isa指针指向了NSKVONotifying_KVOObject

  • 查看NSKVONotifying_KVOObjectsuperclass
    super-class
    发现superclassKVOObject,说明NSKVONotifying_KVOObject是继承与KVOObject的。
  • 查看NSKVONotifying_KVOObject的方法列表
    setidentifie
    发现NSKVONotifying_KVOObject中重写了父类的setIdentifier:。可以大胆猜想,是不是在setIdentifier:中做了对监听者发送消息呢?
  • 查看调用堆栈
    调用堆栈大

注意_NSSetObjectValueAndNotify这个函数,再查看整体的调用堆栈。
调用堆栈小

发现_NSSetObjectValueAndNotify的内部使用了-willChangeValueForKey:didChangeValueForKey:

由上两张图可以发现
在重写的setIdentifier:中调用_NSSetObjectValueAndNotify的函数,内部调用了-willChangeValueForKey:didChangeValueForKey:,从而通知监听者。为了不影响KVOObject的原有逻辑,NSKVONotifying_KVOObject一定会调用父类(KVOObject)的setIdentifier:方法。

总结:

当对一个实例添加监听者时,Foundation框架会动态生成继承与实例对应类的NSKVONotifying_XXX的一个全新的类。并且重写setIdentifier:对监听者进行回调。

You Might Also Like
发表评论