一起养成写作习惯!这是我参与「掘金日新计划 · 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")
}
打印结果
基本上来说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)
})
这里我们把整个对象当成一个运行时的对象,通过动态的访问它的路径下,找到它发生变化的闭包回调从而达到监听的效果,同时不用我们手动管理它的观察者。
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)
打印结果
2.1 创建
observe创建
通过官方的描述可以知道
observe只是对kvo的简单的包装,生成一个KVOObservable的序列
可以观察我们对象属性的路径,包括弱引用和强引用
路径必须只包含“强”属性,否则你可能会因为在dealloc之前没有注销KVO观察者而导致系统崩溃
我们观察Person里的book,设置为弱引用的话会造成崩溃。
observeWeakly
对于observeWeakly我们看下创建
它可以在所有可以使用“observe”的情况下使用,由于它不会保留被观察目标,因此可以用于观察所有权关系未知的任意对象图
它可以用来观察“弱引用”属性
2.1.1 KVOObservable
对于KVOObservable遵循了序列的协议因此可以订阅,其次遵循了KVOObservableProtocol,该协议定义了属性,从而让遵循的类可以拥有该协议的属性,避免了重新定义一个类或者继承的关系。
我们继续看上面的初始化,首先对于target就是我们观察的对象,在闭包中为了避免循环引用这里使用无主引用(相比弱引用,明确了它的生命周期和持有者的一致性)retainTarget进行判读是否强持有
对于我们observe是传的yes
对于我们的observeWeakly进入observeWeaklyKeyPathFor方法
没有进行强持有。
继续我们看KVOObservable的初始化里面的target为我们要观察的对象,对于上面的Person,是面向对象。
2.1.2 observeWeakly 初始化流程
我们看下observeWeakly的初始化
首先对keypath 进行处理之后 创建一个序列后调用finishWithNilWhenDealloc
当我们观察的对象Person正在销毁时候,产生一个空的序列,就不会引用person了。继续看下对于我们观察对象person的属性是weak的时候是如何处理的
通过获取观察对象的属性,进行判断没有属性的话则产生一个错误的序列,否则判断是否存在weak修饰的属性
通过判断属性的符号是否含有
W
之后继续创建一个KVOObservable的序列。
之后我们的序列subscribe后调用这个序列的闭包,进行一些容错处理。
2.2 订阅
我们创建了一个序列KVOObservable包含了我们KVO的一些信息包括target,keypath等。我们继续看下如何订阅的和发送的
我们看下序列自己实现的subscribe
创建我们的观察者,callback的回调,发送信息
观察者继承_RXKVOObserver是oc类实现的,可以发现还是实现了我们三部曲
当我们值发生改变的时候调用callback就会调外面的let observer = KVOObserver(parent: self) {}进行发送,判断我们的value是否有值,发送on事件到的回调。
- dispose
关于我们移除观察者可以发现在调用
dispose的时候会移除我们观察者。
3. 总结
对于observeWeakly和observe使用场景基本上一样,只是observeWeakly会添加一些判断,比如对象是否在销毁等,生成一些空的序列,或者错误序列。通过对kvo的封装产生一个中间者,进行发送序列,回调,销毁。