底层探索--KVO、KVC的本质

2021-10-13  本文已影响0人  永断阎罗

KVO

原理

    NSKVONotifying_对象的类名的组成结构:
    isa
    superclass
    set对象方法---内部调用 _NSSet*ValueAndNotify(如:_NSSetObjectValueAndNotify,*代表监听对象对应的类型,如:int double object等)--详情见下图
    class:[类名 class],重写后返回本身类(屏蔽内部实现,隐藏实际类NSKVONotifying_对象的类名的存在),
    dealloc:做一些销毁操作,
    _isKVOA
    
    
    //实际证明方法如下:
    
    //监听方法
    - (void) observeFunction {
        NSLog(@"class = %@, metaclass = %@ \n",object_getClass(self.model),object_getClass(object_getClass(self.model)));
        //class = ZTKVORootM, metaclass = ZTKVORootM
        
        NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionPrior;
        [self.model addObserver:self forKeyPath:@"name" options:options context:nil];
        
        //打印 类对象 和 元类对象(注意:不能使用[self.model class],因为内部已经被重写,不是实际isa指向的类,需用运行时直接获取才是真正的类)
        NSLog(@"class = %@, metaclass = %@ \n",object_getClass(self.model),object_getClass(object_getClass(self.model)));
        //打印结果:class = NSKVONotifying_ZTKVORootM, metaclass = NSKVONotifying_ZTKVORootM
        
        //打印方法
        NSLog(@"Method = %p",[self.model methodForSelector:@selector(setName:)]);
        //打印结果:p (IMP)0x223413e04
        //(IMP) $3 = 0x0000000223413e04 (Foundation`_NSSetObjectValueAndNotify)
        
        //证明:添加监听后,运行时新增了类NSKVONotifying_ZTKVORootM,并重新了监听的属性的set方法
        
        //打印监听后的类 和 没监听类的对象方法
        ZTKVORootM *notModel = [[ZTKVORootM alloc]init];
        
        [self getAllMethodListWithClass:object_getClass(notModel)];
        [self getAllMethodListWithClass:object_getClass(self.model)];
        /** 打印如下
         ZTKVORootM下所有的方法列表:
         .cxx_destruct
         name
         setName:
    
         2020-09-23 14:25:06.560550+0800 testApp[862:180083] NSKVONotifying_ZTKVORootM下所有的方法列表:
         setName:
         class
         dealloc
         _isKVOA
         */
    }

    
    //添加依赖:返回(对象中,需监听的属性)
    + (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
        NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
        if ([key isEqualToString:@"person"]) {
            //此时如果person这个对象中的name或age值改变就会触发
            keyPaths = [[NSSet alloc] initWithObjects:@"person.name",@"person.age", nil];
        }
        return keyPaths;
    }
    
    //KVO是否 开启自动监听(值变后自动通知消息)
    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
        return YES;
    }
    
    //获取类-所有的对象方法
    - (void) getAllMethodListWithClass:(Class)class {
        unsigned int count = 0;
        Method *methodList = class_copyMethodList(class, &count);
        
        NSMutableString *methodStr = [[NSMutableString alloc] init];
        for (NSInteger i = 0; i < count; i++) {
            Method method = methodList[i];
            SEL sel = method_getName(method);
            [methodStr appendFormat:@"%@ \n",NSStringFromSelector(sel)];
        }
        NSLog(@"%@下所有的方法列表:\n %@",NSStringFromClass(class),methodStr);
    }
KVO重新的set方法.png KVO的set内部实现.png

面试题

1、 iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)

利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
willChangeValueForKey:
父类原来的setter
didChangeValueForKey:
内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)

2、 如何手动触发KVO?

手动调用willChangeValueForKey:和didChangeValueForKey:

3、 直接修改成员变量会触发KVO么?

不会触发KVO,只有调用了setter方法才会触发

KVC

KVC的set方法原理.png KVC的get方法原理.png

面试题

1、 通过KVC修改属性会触发KVO么?

会触发KVO。
原理:不论有没有实现setter方法,都会触发KVO,内部应该实现了:调用willChangeValueForKey:和didChangeValueForKey:

上一篇下一篇

猜你喜欢

热点阅读