RxSwift学习-16-RxSwift中KVO的使用和分析

1,438 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第19天,点击查看活动详情

  • 本文主要介绍RxSwift中KVO的使用,和原理的分析

1. Swift中KVO的使用

对于kvo在oc中我们通过键值观察进行监测对象属性的变化,通过3步曲进行监听。

  • kvo相当于我们实时监测某一个关心的值,基于这个值的setter方法监听的,当调用这个值告诉观察者,这个值要改变了,不用的时候移除观察。

  • kvo内部实现是通过一个中间类去操作,中间类的setter主要是通知观察者值的改变调用父类的setter方法,实际去改变。这样设计相当于一个中间类当成一个管家,外面只要吩咐一下,具体执行交给管家,降低了耦合性

  • kvo移除主要是还原isa,把实例对象的isa由中间类还原到本类,中间类不会销毁,方便下次关联。
    关于kvo的原理可以看下我之前的文章: kvo原理探索 那么对于swift怎么实现kvo呢?

1.1 方式1

类似我们oc的方式,添加,观察,移除。


class Person: NSObject {

    

    @objc dynamic var name:String = "jack"

}

var p = Person()
//添加

self.p.addObserver(self, forKeyPath: "name", options: .new, context: nil)
       
//响应
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

        print(change as Any)    
}       
deinit{
        //移除
self.p.removeObserver(self, forKeyPath: "name", context: nil)

        print("DetailViewController dealloc")

}

打印结果 image.png

基本上来说swift还是对oc的kvo进行封装,对象属性@objc dynamic修饰为了具有oc中的运行时能力

1.2 方式2

swift对kvo进行了封装方便我们可以快发开发

@objc dynamic var p = Person()
self.kvo = observe(\.p.name, options: .new, changeHandler: { (_,change) in

            print(change.newValue as Any)

        })

这里我们把整个对象当成一个运行时的对象,通过动态的访问它的路径下,找到它发生变化的闭包回调从而达到监听的效果,同时不用我们手动管理它的观察者。

image.png

2. RxSwift使用KVO

RxSwift对kvo也进行了封装,我们使用也是比较简单

self.p.rx.observeWeakly(String.self, "name")

            .subscribe(onNext: {print($0 as Any)})

            .disposed(by: disposeBag)

我们也可以使用observe

self.p.rx.observe(String.self, "name")

            .subscribe(onNext: {print($0 as Any)})

            .disposed(by: disposeBag)

打印结果

image.png

2.1 创建

observe创建 image.png 通过官方的描述可以知道observe只是对kvo的简单的包装,生成一个KVOObservable的序列
可以观察我们对象属性的路径,包括弱引用强引用 路径必须只包含“强”属性,否则你可能会因为在dealloc之前没有注销KVO观察者而导致系统崩溃

image.png 我们观察Person里的book,设置为弱引用的话会造成崩溃。

  • observeWeakly

对于observeWeakly我们看下创建

image.png

它可以在所有可以使用“observe”情况下使用,由于它不会保留被观察目标,因此可以用于观察所有权关系未知的任意对象图 它可以用来观察“弱引用”属性

2.1.1 KVOObservable

image.png

对于KVOObservable遵循了序列的协议因此可以订阅,其次遵循了KVOObservableProtocol,该协议定义了属性,从而让遵循的类可以拥有该协议的属性,避免了重新定义一个类或者继承的关系。

image.png

我们继续看上面的初始化,首先对于target就是我们观察的对象,在闭包中为了避免循环引用这里使用无主引用(相比弱引用,明确了它的生命周期和持有者的一致性)retainTarget进行判读是否强持有 对于我们observe是传的yes

image.png

对于我们的observeWeakly进入observeWeaklyKeyPathFor方法

image.png

没有进行强持有。
继续我们看KVOObservable的初始化里面的target为我们要观察的对象,对于上面的Person,是面向对象。

2.1.2 observeWeakly 初始化流程

我们看下observeWeakly的初始化

image.png

首先对keypath 进行处理之后 创建一个序列后调用finishWithNilWhenDealloc

image.png

当我们观察的对象Person正在销毁时候,产生一个空的序列,就不会引用person了。继续看下对于我们观察对象person的属性weak的时候是如何处理的

image.png

通过获取观察对象的属性,进行判断没有属性的话则产生一个错误的序列,否则判断是否存在weak修饰的属性

image.png 通过判断属性的符号是否含有W

之后继续创建一个KVOObservable的序列。

image.png

之后我们的序列subscribe后调用这个序列的闭包,进行一些容错处理。

2.2 订阅

我们创建了一个序列KVOObservable包含了我们KVO的一些信息包括target,keypath等。我们继续看下如何订阅的和发送的

我们看下序列自己实现的subscribe

image.png

创建我们的观察者,callback的回调,发送信息

image.png

观察者继承_RXKVOObserver是oc类实现的,可以发现还是实现了我们三部曲

image.png

当我们值发生改变的时候调用callback就会调外面的let observer = KVOObserver(parent: self) {}进行发送,判断我们的value是否有值,发送on事件到的回调。

  • dispose 关于我们移除观察者可以发现在调用dispose的时候会移除我们观察者。

3. 总结

对于observeWeaklyobserve使用场景基本上一样,只是observeWeakly会添加一些判断,比如对象是否在销毁等,生成一些空的序列,或者错误序列。通过对kvo的封装产生一个中间者,进行发送序列,回调,销毁。