zoukankan      html  css  js  c++  java
  • KVC/KVO总结

    KVC(键值编码)

    动态设置:

    setValue:属性值 forKey:属性名(用于简单路径)

    setValue:属性值 forKeyPath:属(用于复合路径,例如Person有一个Account类型的属性,那么person.account就是一个复合属性)

    动态读取:

    valueForKey:属性名

    valueForKeyPath:属性名(用于复合路径)

     

    KVO(键值监听)

    常规使用

    1.注册指定key路径的监听器

    - (void)addObserver:(NSObject *)anObserver
             forKeyPath:(NSString *)keyPath
                options:(NSKeyValueObservingOptions)options
                context:(void *)context

    方法说明

    作用对象:被监听对象

    参数说明:

    anObserver:监听者

    keyPath:被监听的属性

    options:监听选项

    context:任意的额外数据(上下文数据)

    参数解析:

    options:监听选项,可以是NSKeyValueObservingOptions选项的组合。

    typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {
        //提供属性新值(修改后的值)
        NSKeyValueObservingOptionNew = 0x01,
    
        //提供属性旧值(修改前的值)
        NSKeyValueObservingOptionOld = 0x02,
    
        //如果指定,则在添加观察者的时候立即发送一个通知给观察者,
        // 并且是在注册观察者方法返回之前
        NSKeyValueObservingOptionInitial  = 0x04,
    
        // 如果指定,则在每次修改属性时,会在修改通知被发送之前预先发送一条通知给观察者,
        // 这与-willChangeValueForKey:被触发的时间是相对应的。
        // 这样,在每次修改属性时,实际上是会发送两条通知。
        NSKeyValueObservingOptionPrior  = 0x08
    };

    context:可以将这些数据作为上下文数据,它会传递给观察者对象的observeValueForKeyPath:ofObject:change:context:方法。这个参数的意义在于用于区分同一对象监听同一属性(从属于同一对象)的多个不同的监听。(监听不会覆盖,只会并存)

    2.删除指定key路径的监听器

    - (void)removeObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath
    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context

    方法说明

    作用对象:被监听对象

    参数说明:

    anObserver:监听者

    keyPath:监听属性

    context:上下文数据,与注册监听方法中的context对应。

    3.监听回调

    - (void)observeValueForKeyPath:(NSString *)keyPath
                          ofObject:(id)object
                            change:(NSDictionary *)change
                           context:(void *)context

    方法说明:

    使用:重写该方法

    参数说明:

    keyPath:监听属性

    object:监听对象(keyPath所属的对象)

    change:监听属性的变化(字典类型)

    context:数据上下文,从注册监听方法中传过来

    参数解析:

    change:记录监听属性的变化

    // 属性变化的类型,是一个NSNumber对象,包含NSKeyValueChange枚举相关的值
    NSString *const NSKeyValueChangeKindKey;
    
    // 属性的新值。当NSKeyValueChangeKindKey是 NSKeyValueChangeSetting,
    // 且添加观察的方法设置了NSKeyValueObservingOptionNew时,我们能获取到属性的新值。
    // 如果NSKeyValueChangeKindKey是NSKeyValueChangeInsertion或者NSKeyValueChangeReplacement,
    // 且指定了NSKeyValueObservingOptionNew时,则我们能获取到一个NSArray对象,包含被插入的对象或
    // 用于替换其它对象的对象。
    NSString *const NSKeyValueChangeNewKey;
    // 属性的旧值。当NSKeyValueChangeKindKey是 NSKeyValueChangeSetting, // 且添加观察的方法设置了NSKeyValueObservingOptionOld时,我们能获取到属性的旧值。 // 如果NSKeyValueChangeKindKey是NSKeyValueChangeRemoval或者NSKeyValueChangeReplacement, // 且指定了NSKeyValueObservingOptionOld时,则我们能获取到一个NSArray对象,包含被移除的对象或 // 被替换的对象。 NSString *const NSKeyValueChangeOldKey;
    // 如果NSKeyValueChangeKindKey的值是NSKeyValueChangeInsertion、NSKeyValueChangeRemoval // 或者NSKeyValueChangeReplacement,则这个key对应的值是一个NSIndexSet对象, // 包含了被插入、移除或替换的对象的索引 NSString *const NSKeyValueChangeIndexesKey; // 当指定了NSKeyValueObservingOptionPrior选项时,在属性被修改的通知发送前, // 会先发送一条通知给观察者。我们可以使用NSKeyValueChangeNotificationIsPriorKey // 来获取到通知是否是预先发送的,如果是,获取到的值总是@(YES) NSString *const NSKeyValueChangeNotificationIsPriorKey;

    手动发送通知

      KVO是怎么做到对对象属性值的监听的呢? NSObject提供了一个NSKeyValueObserving协议的默认实现,这个协议使得对象属性在修改时具备了发送通知的能力。 当一个对象的属性发生修改时,KVO会调用下面两个方法发送修改通知。一旦修改的属性被监听,通知就会发送给监听者。

      理论上,子类不应该重写这两个方法,若要重写一定要在方法中调用父类的该方法,否则修改消息不会发送给监听者。但是如果只是监听自身的某个属性,则可以直接在这两个方法中对属性值的改变做出需要的响应,而不需要继续将消息发送出去,这时候无须为属性注册监听者。

    //即将改变(值还未改变)
    - (void)willChangeValueForKey:(NSString *)key{
        NSLog(@"%@",key);
    }
    
    //已经改变
    - (void)didChangeValueForKey:(NSString *)key{
        NSLog(@"%@",key);
    }

    这两个方法是KVO在对象属性值发生修改时发送通知的,默认情况下属性值改变会默认调用这两个方法发送通知。为提高灵活性,我们也可以手动发送通知。如果想对某个对象属性设置不自动发送值改变时的通知,重写下面方法

    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key

    判断key是否是对应对象属性名,是的话返回NO,这样就取消了这个对象属性自动发送修改通知的能力。

    注意:

    1.对于不是目标属性名的key,调用super的该方法,避免对其他属性的处理方法做了意外的修改。

    2.设置了手动通知之后,只需在需要的地方视情况手动调用那两个发送通知的方法即可。

     

    被监听对象依赖于别的属性

      假如要监听一个属性,这个属性的值依赖于其他若干个属性,这个属性称为计算属性,被依赖的属性中任意一个值发生变化都会导致计算属性的值改变。对于这种情况,一个做法是放弃计算属性,转而对被依赖的每个属性设置监听,然而这种方法会使情况变得复杂。这里KVO提供了更好的处理方法。

      首先设置好计算属性与依赖属性之间的依赖关系,这个依赖关系在计算属性的getter方法中体现,暂不知道还有没有什么别的设置依赖关系的方法。

    //建立依赖关系(viewColor计算属性依赖于rColor、gColor、bColor)

    - (UIColor *)viewColor{
        return [UIColor colorWithRed:self.rColor/255.0 green:self.gColor/255.0 blue:self.bColor/255.0 alpha:1.0];
    }

    设置好依赖关系之后,要做的就是告诉KVO这个依赖关系的存在,重写方法:

    + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key

    key为依赖属性名的情况下返回一个包含了依赖属性名的集合对象。也可以直接对依赖属性作用方法(以viewColor为例):

    + (NSSet *)keyPathsForValuesAffectingViewColor 

    做好这些准备工作之后就可以像正常一样对计算属性设置监听了,当任意一个依赖属性值发生改变时,重写了监听回调方法的监听者就会收到通知。

     

    对集合属性的监听

    对于数组、集合这样的集合对象,若是不可变的,我们只能将其当做一个整体来监听,不需要做什么特别处理。若是可变的,我们也将其看成是一个整体,但若想监听到内容的添加、删除、替换,需要用集合代理对象来处理。

    以可变数组为例,直接对数组本身设置监听者,然后添加数组内容,是不会触发通知发送的,因为数组本身是当成一个个体的,内存地址没有变。因此必需借助数组代理对象来实现,这个数组代理对象并不是简单得引用原数组对象,它可以理解成是原可变数组的一个替身,它可以替代原数组做任何原本能做的事,原数组内容也跟着它一起变。

    之所以用代理数组对象可以实现对内容的监听,是因为对代理数组对象的内容添加、删除、替换后,原数组的内存地址发生了改变,这样就能发送通知了。

    创建代理数组对象的方法:

    - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

    该方法定义在KVC中。

    对集合属性的监听也可以手动发送通知,做法和普通属性一样。发送通知方法为

    -willChange:valuesAtIndexes:forKey:
    -didChange:valuesAtIndexes:forKey:

    KVO代码示例见Demo

    参考文档:

    http://www.cnblogs.com/kenshincui/p/3871178.html

    http://southpeak.github.io/blog/2015/04/23/nskeyvalueobserving-kvo/

  • 相关阅读:
    Largest Rectangle in Histogram
    Valid Sudoku
    Set Matrix Zeroes
    Unique Paths
    Binary Tree Level Order Traversal II
    Binary Tree Level Order Traversal
    Path Sum II
    Path Sum
    Validate Binary Search Tree
    新手程序员 e
  • 原文地址:https://www.cnblogs.com/lotheve/p/4850282.html
Copyright © 2011-2022 走看看