KVO-KVC

作者: coder1003 | 来源:发表于2019-12-15 22:21 被阅读0次

    一. KVO概念

    • KVO是Key-value observing的缩写
    • KVO是Objctive-C对观察者设计模式的又一实现
    • 使用了isa混写(isa-swizzling)开实现KVO
    问: KVO是如何实现的?
    答: 创建了一个被观察对象所属类的子类KVONotifying_A类,
    然后重写被观察属性的的setter方法,
    在赋值前调用NSObject的willChangeValueForKey方法,
    在赋值后调用NSObject的didChangeValueForKey方法,
    然后利用isa混写,这样调用class方法的时候,
    你会发现得到仍然是A,这样就隐藏了创建子类的事实。
    
    问:你是如何知道的?如何验证的?
    答:debug时,在调用栈中看到了`KVONotifying_A`这个类,
    然后就觉得应该和A有联系,然后查阅开发者文档看到的。
    可以通过打印所属类类名来验证。
    
    但是???
    确实,直接通过[object class],获取到的仍是A,因为isa混写,但是通过object_getClassName(object)就可以得到KVONotifying_A,可能这是当初没有说清楚的地方。
    再者,我们可以创建一个名为KVONotifying_A的子类,然后发现KVO失效了,这也是一个有力的佐证。
    
    

    二. KVO具体实现机制和原理

    发生在运行时
    当注册类A的一个对象的观察者时, 实际上调用了ObserverForPath这个方法, 系统会在运行时动态创建一个KVONotifying_A(A的子类)的类, 将原来A中isa指针指向KVONotifying_A, 这也是isa混写技术的一个标志
    KVONotifying_A是原来A的子类, 然后重写A的setter方法,达到通知所有观察者的目的


    KVO具体实现机制和原理
    #import "AppDelegate.h"
    #import "MObject.h"
    #import "MObserver.h"
    @interface AppDelegate ()
    
    @end
    
    @implementation AppDelegate
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        
        MObject *obj = [[MObject alloc] init];
        MObserver *observer = [[MObserver alloc] init];
        
        //调用kvo方法监听obj的value属性的变化
        [obj addObserver:observer forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:NULL];
       
        //通过setter方法修改value
        obj.value = 1;
        
        // 1 通过kvc设置value能否生效?
        [obj setValue:@2 forKey:@"value"];
        
        // 2. 通过成员变量直接赋值value能否生效?
        [obj increase];
        
        return YES;
    }
    
    
    #import <Foundation/Foundation.h>
    
    @interface MObject : NSObject
    
    @property (nonatomic, assign) int value;
    
    - (void)increase;
    
    @end
    
    
    #import "MObject.h"
    
    @implementation MObject
    
    - (id)init
    {
        self = [super init];
        if (self) {
            _value = 0;
        }
        return self;
    }
    
    - (void)increase
    {
        //直接为成员变量赋值
        [self willChangeValueForKey:@"value"];
        _value += 1;
        [self didChangeValueForKey:@"value"];
    }
    
    @end
    
    #import "MObserver.h"
    #import "MObject.h"
    @implementation MObserver
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        
        if ([object isKindOfClass:[MObject class]] &&
             [keyPath isEqualToString:@"value"]) {
            
            // 获取value的新值
            NSNumber *valueNum = [change valueForKey:NSKeyValueChangeNewKey];
            NSLog(@"value is %@", valueNum);
        }
    }
    
    
    @end
    

    三. 通过object_getClassName(object)获取类


    NSKVOBNotifying_A的setter实现

    //NSKVOBNotifying_A的setter实现
    - (void)setValue:(id)obj
    {
        //直接为成员变量赋值
        [self willChangeValueForKey:@"keyPath"];
        //调用父类的实现, 也即原类的实现
        [super setValue:obj];
        [self didChangeValueForKey:@"keyPath"];
    }
    
    
    1 通过kvc设置value能否生效?
    • [obj setValue:@2 forKey:@"value"];
    • 通过KVC设置value也调用了setter方法, 而这个setter被重写过了,所以可以通过kvc设置value能够触发KVO
    2. 通过成员变量直接赋值value能否生效?
    • [obj increase];
    • 直接为成员变量赋值, 不能触发KVO,; 手动添加willChangeValueForKey和didChangeValueForKey可以触发KVO
    - (void)increase
    {
        //直接为成员变量赋值, 不能触发KVO
        _value += 1;
    }
    
    - (void)increase
    {
        //直接为成员变量赋值, 手动添加willChangeValueForKey和didChangeValueForKey可以触发KVO
        [self willChangeValueForKey:@"value"];
        _value += 1;
        [self didChangeValueForKey:@"value"];
    }
    

    KVO总结

      1. 使用setter方法改变值KVO才能生效
      1. 使用setValue:forKey:改变值, KVO会生效(KVC底层调用也会调用setter方法, 故可以触发)
      1. 成员变量直接修改, 不能触发KVO; 需要手动添加KVO才会生效

    一篇:KVO&KVC详解

    相关文章

      网友评论

          本文标题:KVO-KVC

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