KVO学习

作者: foreverSun_122 | 来源:发表于2018-04-12 11:03 被阅读0次

KVO机制学习

什么是KVO?

  KVO是Key-Value-Observing的缩写,通过KVO机制对象可以得到其他对象的某个属性的变更通知。这种机制在MVC模式下显得更为重要,KVO可以让视图对象经过控制器观察模型对象的变更从而做出更新等操作。KVO不仅是Objective-C对观察者模式(Observer Pattern)的实现,也是Cocoa Binding的基础。

KVO怎么用?

  KVO这一机制是基于NSKeyValueObserving协议的,Cocoa通过这个协议为所有遵循协议的对象提供了自动观察属性变化的能力。在NSObject中已经为我们实现了这一协议,所以我们不必去实现这个协议。

使用步骤:
1.注册观察者,实施监听;

//observer:观察者
//keyPath: 被观察的属性名称
//options: 观察属性的新值、旧值等的一些配置(枚举值,可以根据需要设置,例如这里可以使用两项)
//第四个参数context: 上下文,可以为kvo的回调方法传值(例如设定为一个放置数据的字典)
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

2.观察者实现回调方法,在回调方法中处理属性发生的变化;

//keyPath:属性名称
//object:被观察的对象
//change:变化前后的值都存储在change字典中
//context:注册观察者时,context传过来的值
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context;

3.移除观察者:

//observer:观察者
//keyPath:属性名称
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

实例:

//被观察者
@interface TestClass : NSObject

@property (nonatomic, assign)int x;
@property (nonatomic, assign)int y;

@end

@implementation TestClass

@end

//观察者
@interface ObserverClass: NSObject

@property (nonatomic, strong) TestClass *obj;

- (instancetype)initWith:(TestClass *)obj;

@end

@implementation ObserverClass

- (instancetype)initWith:(TestClass *)obj
{
    if(self = [super init])
    {
        //不能写_obj = obj; 外部的obj发生变化,内部的_obj也会同步变化,因为_obj与obj都指向同一地址
        _obj = [[TestClass alloc]init];
        _obj.x = obj.x;
        _obj.y = obj.y;
    }
    return self;
}

//观察者类需要实现的回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if([keyPath isEqualToString:@"x"] && object)
    {
        _obj.x = ((TestClass *)object).x;
    }
    else if([keyPath isEqualToString:@"y"] && object)
    {
        _obj.y = ((TestClass *)object).y;
    }
}

@end

//在main方法中定义观察者对象和被观察者对象
int main(int argc, char * argv[]) {
    @autoreleasepool {
        TestClass *obj = [[TestClass alloc]init];
        obj.x = 1;
        obj.y = 2;
        
        ObserverClass *observer = [[ObserverClass alloc]initWith:obj];
        
        NSLog(@"before adding Observer");
        NSLog(@"Observer x:%d", observer.obj.x);
        NSLog(@"Observer y:%d", observer.obj.y);
        
        //添加观察者
        [obj addObserver:observer forKeyPath:@"x" options:NSKeyValueObservingOptionNew context:nil];
        [obj addObserver:observer forKeyPath:@"y" options:NSKeyValueObservingOptionNew context:nil];
        
        obj.x = 3;
        obj.y = 4;
        NSLog(@"after adding Observer");
        NSLog(@"Observer x:%d", observer.obj.x);
        NSLog(@"Observer y:%d", observer.obj.y);
        
        //移除观察者
        [obj removeObserver:observer forKeyPath:@"x"];
        [obj removeObserver:observer forKeyPath:@"y"];
        return 0;
    }
}

为什么使用KVO?

  1. 我们创建一两个setter方法感觉没什么,但是如果要观察的属性非常多,那么还能一一重写setter方法来实现吗?想必大家心里已有了答案,但是利用KVO则能很好的解决上述问题。

  2. 我们自定义的类是很容易改写setter方法的,但是如果你是用一个已经编译好了的类库时要监控其中一个属性时怎么办?难道还要去重写setter方法?如果使用KVO则很轻松解决问题。

  3. 使用KVO能够方便的记录变化前的值和变化后的值,不使用KVO你还要自己来解决这些问题。

  4. KVO让你的代码看起来更加简洁清晰易于维护。

KVO的特点

  观察者观察的是属性,只有遵循 KVO 变更属性值的方式才会执行KVO的回调方法,例如是否执行了setter方法、或者是否使用了KVC赋值。
  如果赋值没有通过setter方法或者KVC,而是直接修改属性对应的成员变量,例如:仅调用_name = @"newName",这时是不会触发kvo机制,更加不会调用回调方法的。
所以使用KVO机制的前提是遵循 KVO 的属性设置方式来变更属性值。

KVO的实现原理

  KVO 的实现也依赖于 Objective-C 强大的 Runtime,Apple 使用了 isa 混写(isa-swizzling)来实现 KVO。swizzling是不是很熟悉,在Method Swizzling中我们修改了Method的IMP指向,isa-Swizzling就是修改了isa指针的指向。
  首次观察某个object时,runtime会创建一个新的继承原先class的subclass。在这个新的class中,它重写了所有被观察的key,然后将object的isa指针指向新创建的class(这个指针告诉Objective-C运行时某个object到底是哪种类型的object)。所以object神奇地变成了新的子类的实例。
  这些被重写的方法实现了如何通知观察者们。当改变一个key时,会触发setKey方法,但这个方法被重写了,并且在内部添加了发送通知机制。(当然也可以不走setXXX方法,比如直接修改iVar,但不推荐这么做)。
  KVO的键值观察通知依赖于 NSObject 的两个方法:

  1. willChangeValueForKey:在被观察属性发生改变之前调用,通知系统该 keyPath 的属性值即将变更
  2. didChangevlueForKey:属性改变发生后调用,通知系统该 keyPath 的属性值已经变更;

  之后观察者实现的observeValueForKey:ofObject:change:context:也会被调用。并且重写观察属性的setter方法这种继承方式的注入是在运行时而不是编译时实现的。
  有意思的是:苹果不希望这个机制暴露在外部。除了setters,这个动态生成的子类同时也重写了-class方法,依旧返回原先的class!如果不仔细看的话,被KVO过的object看起来和原先的object没什么两样。

以下是在网上找到的一张图,比较形象地描述了KVO的实现原理:


KVO实现原理

参考

相关文章

  • KVO学习

    KVO机制学习 什么是KVO?   KVO是Key-Value-Observing的缩写,通过KVO机制对象可以得...

  • OC的KVO学习记录(2)

    仿写KVO的实现KVO原理参考可前一篇OC的KVO学习记录代码github地址:Sameny仿写KVO 注:关键代...

  • KVO学习笔记

    1.KVO初探学习2.KVO 底层原理探索 1.KVO初探学习 移除观察者的重要性 (IOS11之后说不移除是不对...

  • KVO&KVC分析

    KVO和KVC应用还是比较广泛的。所以,今天我们就重新再学习下它们,以加深记忆。 一、KVO 1、KVO(key-...

  • KVO学习

    KVO,俗称键值监听,可以用于监听某个对象属性值的改变。 先简单的演示下KVO的使用方式 点击手机屏幕,打击结果如...

  • KVO学习

    部分代码均可参考AFNetworking文件 1.KVO监听属性(自动监听) 1.1.注册监听 1.2.监听回调 ...

  • KVO 学习

    KVO key-Value Observing 键值监听 ,可以用于监听某个对象属性值的改变 使用了KVO监听的对...

  • KVC、KVO的本质

    这篇文章介绍KVC、KVO的本质。如果你对KVC、KVO不了解,推荐先查看其用法:KVC和KVO学习笔记[http...

  • KVO初探

    最好的学习文档是 KVO苹果官方文档 KVO三步曲 添加观察者 observer:注册KVO通知的对象。观察者必须...

  • iOS 底层探索之KVO

    KVO是Objective-C中的键值监听策略,本篇能学习到以下知识点1、KVO的使用方法大全2、KVO的底层如何...

网友评论

      本文标题:KVO学习

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