KVO

2019-12-11  本文已影响0人  张_何

意思是KVO是基于isa-swizzling实现的,在注册观察者的时候,会修改观察对象的isa指向。不要使用isa指针来判断类的关系,而应该使用class方法。

KVO 的本质

NSKVONotifying_XXX窥探
setter 方法窥探
    NSLog(@"监听前:%p", [self.p methodForSelector:@selector(setAge:)]);//这个方法返回一个方法实现的地址, 通过终端 p (IMP)方法地址 可以知道调用方法的信息
    [self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:NULL];
    NSLog(@"监听后:%p", [self.p methodForSelector:@selector(setAge:)]);

我们在对象 p 的 age 属性添加监听对象前后分别打印 setAge:方法实现的地址指针,然后通过断点,在终端中打印该方法的具体实现

监听前:0x10762e240
监听后:0x7fff2564db08
(lldb) p (IMP)0x10762e240
(IMP) $0 = 0x000000010762e240 (ObjcCode`-[KVOPerson setAge:] at KVOKVCVC.m:16)
(lldb) p (IMP)0x7fff2564db08
(IMP) $1 = 0x00007fff2564db08 (Foundation`_NSSetLongLongValueAndNotify)

通过打印内容看到监听后 setAge 方法内部调用的是Foundation内部的_NSSetLongLongValueAndNotify函数

内部结构窥探

首先实现一个方法来获取一个类内部的所有方法

-(void)printMethodNamesOfClass:(Class)cls {
    unsigned int count;
    Method *methodList = class_copyMethodList(cls, &count);
    NSMutableString *methodNames = [NSMutableString new];
    for (int i = 0; i < count; i++) {
        Method method = methodList[i];
        NSString *methodName = NSStringFromSelector(method_getName(method));
        [methodNames appendString:methodName];
        [methodNames appendString:@", "];
    }
    free(methodList);
    NSLog(@"%@",methodNames);

调用该方法[self printMethodNamesOfClass:object_getClass(self.p)];获得结果setAge:, class, dealloc, _isKVOA,,通过结果我们得出NSKVONotifying_XXX类内部不仅重写的setAge方法,还重写的class dealloc _isKVOA这三个方法.
重写 class 方法可能是不想让外界知道重新生成了子类
重写dealloc方法,做一些实例销毁时的清理工作
添加了_isKVOA方法,来说明自己是kvo类


上一篇 下一篇

猜你喜欢

热点阅读