美文网首页
KVO底层原理分析

KVO底层原理分析

作者: iOS扫地僧 | 来源:发表于2018-07-04 16:22 被阅读0次

    一、 KVO内部实现原理

    • KVO是基于 runtime机制实现的,使用了isa 混写(isa-swizzling

    • 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制

    • 如果原类为Person,那么生成的派生类名为NSKVONotifying_Person (查看方法后面讲解)

    • 每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法

    • 键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey:didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。

    • 补充:KVO的这套实现机制中苹果还偷偷重写了class方法,让我们误认为还是使用的当前类,从而达到隐藏生成的派生类

    二、论证产生的子类

    Book类
    #import <Foundation/Foundation.h>
    
    @interface Book : NSObject
    
    @property (nonatomic, copy) NSString *price;
    
    @property (nonatomic, copy) NSString *name;
    
    @end
    

    重写description方法便于观察产生的子类

    #import "Book.h"
    #import <objc/runtime.h>
    
    @implementation Book
    
    - (NSString *)description {
        NSLog(@"object address : %p \n", self);
        Class objectMethodClass = [self class];
        Class objectRuntimeClass = object_getClass(self);
        Class superClass = class_getSuperclass(objectRuntimeClass);
        NSLog(@"objectMethodClass : %@, ObjectRuntimeClass : %@, superClass : %@ \n", objectMethodClass, objectRuntimeClass, superClass);
        return @"";
    }
    @end
    
    初始化调用
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.abook = [[Book alloc]init];
        NSLog(@"初始化%@",self.abook);
        
        [self.abook addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
        [self.abook addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
    }
    
    打印结果如下
    image.png

    注意:这是在绑定观察者模式之前的初始化调用,此时还没有被观察,此时并没有产生子类

    触发监听调用
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"之前%@",self.abook);
        //触发监听
        [self.abook setValue:@"book" forKey:@"name"];
        [self.abook setValue:@"34" forKey:@"price"];
    }
    
    打印结果如下
    image.png

    注意:这里写了一个简单的触发事件,此时可以观察到,已经产生了一个新的类:NSKVONotifying_Book也可以观察到它的父类是Book

    附加知识

    有些时候程序逻辑的需要,一个类想要实现手动的change notification发送,则必须重写NSObject实现的automaticallyNotifiesObserversForKey:方法,并对需要实现手动发送的key返回NO,其余则调用super。

    对price属性关闭自动发送

    + (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key {
        if ([key isEqualToString:@"price"]) {
            return NO;
        }
        return [super automaticallyNotifiesObserversForKey:key];
    }
    

    对price属性实现手动的change notification发送

    - (void)setPrice:(NSString *)price
    {
        [self willChangeValueForKey:@"price"];
        _price = price;
        [self didChangeValueForKey:@"price"];
    }
    

    本文部分内容转载自:https://www.jianshu.com/p/829864680648

    相关文章

      网友评论

          本文标题:KVO底层原理分析

          本文链接:https://www.haomeiwen.com/subject/qoieuftx.html