美文网首页
KVO 的本质?

KVO 的本质?

作者: 游城十代2dai | 来源:发表于2020-04-07 13:49 被阅读0次
    1. iOS 用什么方式实现对一个对象的 KVO ? (KVO 的本质是什么?)
    • 首先利用 Runtime API 动态创建一个中间类(NSKVONotifying_ + 类名), 并让实例对象的 isa 指针指向这个中间类, 当修改实例对象的属性时, 会调用 Foundation 的 _NSSetxxxValueAndNotify 函数, 该函数内部主要是做 willChangeValueForKey, super 的 setter, didChangeValueForKey, 然后会触发 Observer 监听方法
    1. 如何手动触发 KVO?
    • 实例对象先调用 willChangeValueForKey, 然后再调用 didChangeValueForKey
    1. KVO 是否可以添加成员变量的观察?
    • 可以的, 为成员变量添加 KVO 是可以的但是必须用 KVC 方式赋值

    具体观察代码, 重点observeValueForKeyPath:

    #import "ViewController.h"
    #import "Person.h"
    #import <objc/runtime.h>
    
    /**
     * 1. iOS 用什么方式实现对一个对象的 KVO ? (KVO 的本质是什么?)
     * 首先利用 Runtime API 动态创建一个中间类(NSKVONotifying_ + 类名), 并让实例对象的 isa 指针指向这个中间类, 当修改实例对象的属性时, 会调用 Foundation 的 _NSSetxxxValueAndNotify 函数, 该函数内部主要是做 willChangeValueForKey, super 的 setter, didChangeValueForKey, 然后会触发 Observer 监听方法
     * 2. 如何手动触发 KVO?
     * 实例对象先调用 willChangeValueForKey, 然后再调用 didChangeValueForKey
     */
    
    @interface ViewController ()
    
    @property (nonatomic, strong) Person *person1;
    @property (nonatomic, strong) Person *person2;
    @end
    
    @implementation ViewController
    
    
    - (void)dealloc {
        [self.person1 removeObserver:self forKeyPath:@"age"];
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        self.person1 = Person.new;
        self.person1.age = 10;
        self.person2 = Person.new;
        self.person2.age = 20;
        
        // 添加监听
        [self.person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    }
    
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        self.person1.age = arc4random() % 100 + 1;
        self.person2.age = arc4random() % 100 + 1;
    }
    
    // 当监听对象的属性值改变时, 就会调用
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        /**
         * 打印 person1 的类对象
         * (lldb) p self.person1.isa
         * (Class) $2 = NSKVONotifying_Person
         * Fix-it applied, fixed expression was:
         * self.person1->isa
         *
         * 打印 person2 的类对象
         * (lldb) p self.person2.isa
         * (Class) $3 = Person
         * Fix-it applied, fixed expression was:
         * self.person2->isa
         *
         * 打印所有方法, 可以 llbd 也可以自己实现一个 _printMethodNamesOfClass
         * (lldb) po [$2 _shortMethodDescription]
         * <NSKVONotifying_Person: 0x600000d40870>:
         * in NSKVONotifying_Person:
         *   Instance Methods:
         *      - (void) setAge:(long)arg1; (0x7fff258e518d)        //  ----- 实际会先调用 _NSSetIntValueAndNotify
         *      - (Class) class; (0x7fff258e2fd5)           //  ----- 重写 class 实现, class 方法 底层可能就是 object_getClass, 重写后让读者认为还是 Person 类
         *      - (void) dealloc; (0x7fff258e2d3a)      // ---- 收尾工作
         *      - (BOOL) _isKVOA; (0x7fff258e2d32)
         *   in Person:
         *      Properties:
         *          @property (nonatomic) long age;  (@synthesize age = _age;)
         *      Instance Methods:
         *          - (long) age; (0x10e795f90)
         *          - (void) setAge:(long)arg1; (0x10e795fb0)
         *   (NSObject ...)
         */
    
        [self _printMethodNamesOfClass:object_getClass(object)];
        NSLog(@"%@", change);
    }
    
    // 打印所有方法
    - (void)_printMethodNamesOfClass:(Class)cls {
        // 定义
        unsigned int outCount = 0;
        Method *methodList = NULL;
        Method tempMethod = NULL;
        NSMutableString *strResult = [NSMutableString string];
        NSString *strTemp = [NSString string];
        
        // copy 出方法列表
        methodList = class_copyMethodList(cls, &outCount);
        
        // 遍历
        for (int i = 0; i < outCount; ++i) {
            tempMethod = methodList[i];
            strTemp = NSStringFromSelector(method_getName(tempMethod));
            [strResult appendFormat:@"\n%@", strTemp];
        }
        
        NSLog(@"%@", strResult);
    }
    
    
    @end
    
    

    相关文章

      网友评论

          本文标题:KVO 的本质?

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