KVO

作者: gpylove | 来源:发表于2019-11-20 18:26 被阅读0次

              KVO在OC中是实现键值(key-value-observing)观察的方式,在设计模式中是典型的观察者模式, 当观察者将被观察者的某个属性设置为观察的对象时,若被观察的该属性值发生变化时,就会触发观察者对象所实现的KVO接口方法,从而达到通知观察者的目的。

            简单来说KVO可以通过监听key,来获得value的变化,用来在对象之间监听状态变化。KVO的定义都是对NSObject的扩展来实现的,Objective-C中有个显式的NSKeyValueObserving类别名,所以对于所有继承了NSObject的类型,都能使用KVO。

             KVO的实现是基于iOS runtime机制的isa-swizzling(指针替换),当一个对象的属性被注册成被观察对象时,会生成一个中间类继承自该类,然后将该类的isa指针指向新生成的子类,这样被观察的对象就变成了这个中间类,同时重写了被观察属性的setter方法,当新对象的属性发生变化时,则会依次通知注册的观察者对象。

    自动KVO(默认方式)

    a、添加观测者

    -(void)addObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(nullablevoid*)context;

    b、当观测的属性值发生改变时调用的函数

    -(void)observeValueForKeyPath:(nullable NSString*)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey,id>*)change context:(nullablevoid*)context;

    c、移除观察者

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

            注意,如果被观察的属性没有调用Setter方法,而是通过直接访问其实例变量(_下划线方法)是不会触发回调的。

    手动KVO

            KVO在属性发生改变的时候默认是自动调用的,如果需要手动的控制这个调用时机,那么在被观察的对象.m文件中调用如下方法:

    +(BOOL)automaticallyNotifiesObserversForKey:(NSString*)key{

    returnYES;//默认,自动模式

    returnNO;//手动模式

    }

    同时在属性变化之前,调用:

    -(void)willChangeValueForKey:(NSString*)key;

    在属性变化之后,调用:

    -(void)didChangeValueForKey:(NSString*)key;

            其实无论属性的值是否发生改变,是否调用Setter方法,只要调用了willChangeValueForKey:和didChangeValueForKey:就会触发回调。

    依赖键KVO

    如果在当前Person类中引入另外一个Dog类:

    @interface Dog : NSObject

    @property (copy, nonatomic) NSString *age;

    @property (copy, nonatomic) NSString *name;

    @end

    那么此时我们怎么通过Person来观察Dog类的age属性呢?

    [_p addObserver:self forKeyPath:@"dog.age" options:NSKeyValueObservingOptionNew    context:nil];

    我们现在希望,只要Dog类中有属性的变化,就会通知到Person类,如果我们每一个属性都添加一遍观察者会很麻烦,这里就需要用到属性依赖。我们在Person类的.m中添加一个方法:

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

    {

        NSSet *keyPath = [super keyPathsForValuesAffectingValueForKey:key];

        if([keyisEqual:@"dog"]) {

            keyPath = [[NSSetalloc]initWithObjects:@"_dog.age",@"_dog.name",nil];

        }

        returnkeyPath;

    }

    同时在添加观察者时,不用对dog具体的属性添加,直接对dog进行观察。

    [_p addObserver:self forKeyPath:@"dog" options:NSKeyValueObservingOptionNew context:nil];

            设置键之间的依赖关系+(NSSet<NSString*>*)keyPathsForValuesAffectingValueForKey:(NSString*)key;当添加一个被观察的keyPath,这个方法就会走。其大概意思是就是:用一组键的值去影响另一个键值。注意该方法一定要考虑非指定key时的情况要调用[super keyPathsForValuesAffectingValueForKey:key],不能影响其他的情况。

    相关文章

      网友评论

          本文标题:KVO

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