美文网首页
KVC/KVO的理解

KVC/KVO的理解

作者: Light413 | 来源:发表于2020-03-25 20:52 被阅读0次
    iOS开发基础.png

    KVC

    KVC实现了基于KEY访问对象属性的一套查找规则,可以直接操作对象的属性,变量即使设置为私有的也一样访问。

    要操作一个对象首先要把它找出来,KVC底层机制实现了其一步步的查找规则。了解它的处理机制更有利于设计我们的类,方法实现等操作。

    获取值操作的查找方式

    通过valueForKey:方法获取值

    • 1、搜索实例的访问器方法,调用方法名为get<Key>, <key>, is<Key>, or _<key>的方法。如果找到了就返回 -> 第5步,找不到继续下一步。

    • 2、继续查找是否含有NSArray类的方法:countOf<Key> , objectIn<Key>AtIndex: , <key>AtIndexes:方法

    //必须实现
    countOf<Key> 
    
    //必须实现其中一个
    objectIn<Key>AtIndex:
    <key>AtIndexes:
    

    如果存在,创建一个集合代理对象,这个对象就像和NSArray一样来使用,返回该对象。否则下一步。

    • 3、查找是否全部包含以下NSSet类的方法
    countOf<Key>, 
    enumeratorOf<Key>, 
    and memberOf<Key>:
    
    

    如果存在,创建一个集合代理对象,就像和NSSet一样来使用,返回该对象。否则下一步。

    • 4、没有找到可以访问的方法;

    判断是否可以直接访问实例变量,

     + (BOOL)accessInstanceVariablesDirectly
    

    如果返回true,按照以下顺序查找:

    _<key>, _is<Key>, <key>, is<Key>

    找到了执行第5步 , 没找到执行第6步。

    • 5、找到了,结果返回分为三种情况:
      如果值是一个对象,直接返回。
      如果是NSNumber支持的数值类型,包装成NSNumber对象,返回。
      如果不是NSNumber支持的数值类型,包装成NSValue对象,返回。
    • 6、没找到,默认调用valueForUndefinedKey: 抛出异常
      我们可重写该方法实现,至此整个查找流程结束。
    设置操作的查找方式

    通过setValue:forKey:方法设置操作

    • 1、依次查找set<Key>: 、 _set<Key>方法名,找到后直接调用赋值然后完成返回,否则下一步2。
    • 2、没有找到可访问的方法:

    判断是否可以直接访问实例变量,

     + (BOOL)accessInstanceVariablesDirectly
    

    如果返回true,按照以下顺序查找:

    _<key>, _is<Key>, <key>, is<Key>

    如果找到指定变量名的实例变量直接赋值然后完成返回。否则下一步3

    • 3、调用setValue:forUndefinedKey:抛出异常。

    数组的查找方法为 mutableArrayValueForKey:,其查找过程类似。

    支持集合操作
    • @avg 平均值

    • @count 个数

    • @max 最大值

    • @min 最小值

    • @sum 和

    //////集合操作
    NSArray * arr = @[@10 , @13 , @5 , @20 , @30];
    
    NSInteger count =  [[arr valueForKeyPath:@"@count"] integerValue];
    NSLog(@"count:%ld" , (long)count);
    
    NSInteger min =  [[arr valueForKeyPath:@"@min.integerValue"] integerValue];
    NSLog(@"min:%ld" , (long)min);
    
    NSInteger max =  [[arr valueForKeyPath:@"@max.integerValue"] integerValue];
    NSLog(@"max:%ld" , (long)max);
    
    NSInteger sum =  [[arr valueForKeyPath:@"@sum.integerValue"] integerValue];
    NSLog(@"sum:%ld" , (long)sum);
    
    NSInteger avg =  [[arr valueForKeyPath:@"@avg.integerValue"] integerValue];
    NSLog(@"avg:%ld" , (long)avg);
        
    
    当对子类继承自父类的属性使用KVC时,查找顺序如下:

    子类的get方法->父类的get方法->accessInstanceVariablesDirectly是否返回YES-->子类的成员变量>父类的成员变量。此外还支持路径查询。

    KVC优缺点:

    优点:可以根据对象属性名直接访问,不管是否设置为私有都可取到。

    缺点:解析key字符串,一步步的查找难免不费时间。


    KVO

    KVO是观察者模式的一个实现,利用runtime的机制,当对一个对象进行观察时,会在运行时创建一个该对象的子类,这个子类一般以NSKVONotifying_xxx(xxx为父类的名字)命名,子类中会重写所有被观察属性的set方法,除了创建子类,还会将该对象的isa指针指向这个子类,当被观察的对象属性修改时,通过isa找到子类,在通过子类的方法列表找到对应的set方法,set方法是被重写过得,里面实现了相关的通知。

    引用网络一个图说明其流程:

    kvo实现原理.png
    • 使用isa-swizzling技术 ,改变对象所属的类。

    • 当值改变时会调用 willChangeValueForKey:、didChangeValueForKey: 。在一个被观察属性改变之前,调用 willChangeValueForKey: 记录旧的值。在属性值改变之后调用 didChangeValueForKey:。

    • KVO的触发分为自动触发模式和手动触发模式2种。通常我们使用的都是自动通知,注册观察者之后,当条件触发的时候会自动调用-(void)observeValueForKeyPath.

    • 通过kvc设置值时会自动触发kvo观察者事件。

    • 我们可根据上面思路手动触发观察者事件, 然后重写setter方法、通知操作。

    手动模拟KVO,此时要禁止自动触发操作:
    //官方demo
    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
     
        BOOL automatic = NO;
        if ([theKey isEqualToString:@"balance"]) {
            automatic = NO;
        }
        else {
            automatic = [super automaticallyNotifiesObserversForKey:theKey];
        }
        return automatic;
    }
    
    

    修改setter方法:

    - (void)setBalance:(double)theBalance {
        [self willChangeValueForKey:@"balance"];
        _balance = theBalance;
        [self didChangeValueForKey:@"balance"];
    }
    
    //一次修改多个值
    - (void)setBalance:(double)theBalance {
        [self willChangeValueForKey:@"balance"];
        [self willChangeValueForKey:@"itemChanged"];
        _balance = theBalance;
        _itemChanged = _itemChanged+1;
        [self didChangeValueForKey:@"itemChanged"];
        [self didChangeValueForKey:@"balance"];
    }
    

    willChangeValueForKey:告诉观察者值将要改变;
    didChangeValueForKey:告诉观察者值已经改变;这两个方法必须成对出现。

    KVO优缺点:

    优点:

    • 借助系统框架可以很方便检测对象属性值变化实现简的单数据同步,支持路径观察。
    • 支持一对多。
    • 用key paths来观察属性,因此也可以观察嵌套对象。
    • 能够提供观察的属性的最新值以及先前值。
    • 完成了对观察对象的抽象,因为不需要额外的代码来允许观察值能够被观察。

    缺点:

    • 观察的属性必须使用strings来定义。因此在编译器不会出现警告以及检查。
    • 对属性重构将导致我们的观察代码不再可用。

    参考

    Key-Value Coding Programming Guide
    Key-Value Observing Programming Guide
    好好看看 KVC && KVO-https://juejin.im/post/5c52acabe51d4551c75ffa5b

    相关文章

      网友评论

          本文标题:KVC/KVO的理解

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