KVO 的本质?

2020-04-07  本文已影响0人  游城十代2dai
  1. iOS 用什么方式实现对一个对象的 KVO ? (KVO 的本质是什么?)
  1. 如何手动触发 KVO?
  1. KVO 是否可以添加成员变量的观察?

具体观察代码, 重点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

上一篇 下一篇

猜你喜欢

热点阅读