KVO底层原理

一、KVO基础操作

添加观察者

    [_myClass addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    //属性名
    NSLog(@"keyPath = %@",keyPath);
    //观察属性的对象,self
    NSLog(@"object = %@",object);
    //kind : 1 (新值) new : 属性的新值
    //NSKeyValueChangeSetting = 1,改变的操作
    //NSKeyValueChangeInsertion = 2,插入的操作 数组
    //NSKeyValueChangeRemoval = 3,移除的操作 数组
    //NSKeyValueChangeReplacement = 4,代替的操作 数组
    NSLog(@"change = %@",change);
    NSLog(@"context = %@",context);
}

在dealloc中移除观察者

    [_myClass removeObserver:self forKeyPath:@"name"];
`NSKeyValueObservingOptionNew`:把更改之后的值提供给处理方法

`NSKeyValueObservingOptionOld`:把更改之前的值提供给处理方法

`NSKeyValueObservingOptionInitial`:把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。

`NSKeyValueObservingOptionPrior`:分2次调用。在值改变之前和值改变之后。

自动或者手动打开观察者

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
    //返回NO,手动来触发,否则自动触发。
    return YES;
}

如果设置成手动触发,当要改变属性值的时候需要加入如下代码:

[_myClass willChangeValueForKey:@"name"];
[_myClass setValue:@"KVC" forKey:@"name"];
[_myClass didChangeValueForKey:@"name"];

二、底层实现:运行时替换ISA指针。

用黑魔法进行观察

    NSLog(@"myClassBefore:%@",[self.myClass class]);
    NSLog(@"runtimeBefore:%@",object_getClass(self.myClass));
    [_myClass addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    NSLog(@"myClassAfter:%@",[self.myClass class]);
    NSLog(@"runtimeAfter:%@",object_getClass(self.myClass));

打印结果:

myClassBefore:MyClass
runtimeBefore:MyClass
myClassAfter:MyClass
runtimeAfter:NSKVONotifying_MyClass

对myClass对象的name属性添加观察,runtime会改变myClass对象的类,MyClass->NSKVONotifying_MyClass。如何证明NSKVONotifying_MyClassMyClass的子类?加入头文件#import ,用runtime进行观察。

- (void)viewDidLoad {
    [super viewDidLoad];   
    _myClass = [[MyClass alloc] init];
    NSLog(@"%@",[SecondController findSubClass:[_myClass class]]);
    [_myClass addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    NSLog(@"%@",[SecondController findSubClass:[_myClass class]]);
}
+ (NSArray *)findSubClass:(Class)defaultClass{
    //获取该类中所有类的个数
    int count = objc_getClassList(NULL, 0);
    if (count < 0) {
        return [NSArray array];
    }
    NSMutableArray * output = [NSMutableArray arrayWithObject:defaultClass];
    Class * classes = (Class *)malloc(sizeof(Class) * count);
    //获取所有的类
    objc_getClassList(classes, count);   
    for (int i = 0; i < count; i ++) {
        if (defaultClass == class_getSuperclass(classes[i])) {
            [output addObject:classes[i]];
        }
    }
    free(classes);
    return output;
}

打印结果如下:

(
    MyClass
)
(
    MyClass,
    "NSKVONotifying_MyClass"
)

三、KVO对数组元素的监听

    [_myClass addObserver:self forKeyPath:@"arr" options:NSKeyValueObservingOptionNew context:nil];
    //KVO监听对数组中元素的变化
    [[_myClass mutableArrayValueForKey:@"arr"] addObject:@"add"];
    //这样子添加是监听不到的
    [_myClass.arr addObject:@"add"];

为什么用下面的方法不能监听呢?KVO是基于KVC的,KVO能发送通知,都是通过KVC的方法处理的。
NSLog(@"%@",[[_myClass mutableArrayValueForKey:@"arr"] class]);会自动生成NSMutableArray的子类:NSKeyValueNotifyingMutableArray,并重写了NSMutableArray相关的方法,如add,insert等(用黑魔法,runtime可以列出所有的方法),这里面添加了KVC的通信方法,当调用add这些方法的时候,就会触发通知。在add放里面添加了willChangeValueForKey和didChangeValueForKey方法来触发通知。

你可能感兴趣的:(KVO底层原理)