前言
使用RxSwift
后,经常会用到KVO
,但是并没有去探究底层是如何实现的,是否和swift
的KVO
实现原理相同,本文将探究RxSwift
里的KVO
的底层实现原理。
KVO初探
先看看Swift里KVO的基本使用,三步基本操作
//添加观察者
person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
//观察者响应回调
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
}
//移除观察者
deinit {
person.removeObserver(self, forKeyPath: "name")
}
RxSwift
里的KVO
的基本使用,一步到位,简单粗暴
// 序列
self.person.rx.observeWeakly(String.self, "name")
.subscribe(onNext: { (change) in
print("observeWeakly订阅到了KVO:\(String(describing: change))")
})
.disposed(by: disposeBag)
RxSwift
一般用两种方式使用KVO
rx.observe
: 执行高效,使用的比较多,是一个KVO
机制的简单封装,只能监听Strong
属性,否则有崩溃的风险rx.observeWeakly
: 效率较低一些,因为它要处理对象的释放,防止弱引用。所以它一般使用在weak
属性上,但是能用rx.observer
的时候,也能使用rx.observeWeakly
源码探索
添加观察者
- 点击
observeWeakly
源码进入,并且继续跟踪observeWeaklyKeyPathFor
public func observeWeakly<Element>(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial]) -> Observable<Element?> {
return observeWeaklyKeyPathFor(self.base, keyPath: keyPath, options: options)
.map { n in
return n as? Element
}
}
- 可以看到这里创建了一个
observable
序列对象,点击observeWeaklyKeyPathFor
看看创建的是个什么样的observable
finishWithNilWhenDealloc
是一个容错机制,对象如果被释放就直接发送nil完成,没有观察的必要,点进去看它的方法内容能够明白
private func observeWeaklyKeyPathFor(_ target: NSObject, keyPath: String, options: KeyValueObservingOptions) -> Observable<AnyObject?> {
let components = keyPath.components(separatedBy: ".").filter { $0 != "self" }
let observable = observeWeaklyKeyPathFor(target, keyPathSections: components, options: options)
.finishWithNilWhenDealloc(target)
if !options.isDisjoint(with: .initial) {
return observable
}
else {
return observable
.skip(1)
}
}
- 这里主要做了几步重要的操作
weak var weakTarget: AnyObject? = target
把传入进来的target
对象设置为用weak
修饰
private func observeWeaklyKeyPathFor(
_ target: NSObject,
keyPathSections: [String],
options: KeyValueObservingOptions
) -> Observable<AnyObject?> {
weak var weakTarget: AnyObject? = target
let propertyName = keyPathSections[0]
let remainingPaths = Array(keyPathSections[1..<keyPathSections.count])
let property = class_getProperty(object_getClass(target), propertyName)
if property == nil {
return Observable.error(RxCocoaError.invalidPropertyName(object: target, propertyName: propertyName))
}
let propertyAttributes = property_getAttributes(property!)
// should dealloc hook be in place if week property, or just create strong reference because it doesn't matter
let isWeak = isWeakProperty(propertyAttributes.map(String.init) ?? "")
let propertyObservable = KVOObservable(object: target, keyPath: propertyName, options: options.union(.initial), retainTarget: false) as KVOObservable<AnyObject>
// KVO recursion for value changes
// 后面代码省略,主要是一个封装容错处理
}
- 使用了
isWeak
判断这个监听的属性是否是weak
, 通过判断属性值里有没有W
来判断,通过断点发现监听的name
属性是没有包含W
的
private func isWeakProperty(_ properyRuntimeInfo: String) -> Bool {
return properyRuntimeInfo.range(of: ",W,") != nil
}
- 看到这里创建的是一个
KVOObservable
,点进去发现它在初始化方法里保存了需要监听的属性和对象等内容,是一个KVO保存者
- 这里实现了两个协议,
ObservableType
让其具有序列的特性,KVOObservableProtocol
让其扩展几个协议属性,是面向协议编程的思路
fileprivate final class KVOObservable<Element>
: ObservableType
, KVOObservableProtocol {
typealias Element = Element?
unowned var target: AnyObject
var strongTarget: AnyObject?
var keyPath: String
var options: KeyValueObservingOptions
var retainTarget: Bool
init(object: AnyObject, keyPath: String, options: KeyValueObservingOptions, retainTarget: Bool) {
self.target = object
self.keyPath = keyPath
self.options = options
self.retainTarget = retainTarget
if retainTarget {
self.strongTarget = object
}
}
//其余代码先省略
}
订阅并发送响应
subscribe
的流程和我们之前探索的RX流程基本一样,非常简单,直接省略这个流程来到上一步最后的KVOObservable
里的subscribe
方法- 发现这里创建的是一个
KVOObserver
观察者,并跟随一个闭包,点进去看看
fileprivate final class KVOObservable<Element>
: ObservableType
, KVOObservableProtocol {
func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element? {
let observer = KVOObserver(parent: self) { value in
if value as? NSNull != nil {
observer.on(.next(nil))
return
}
observer.on(.next(value as? Element))
}
return Disposables.create(with: observer.dispose)
}
}
- 发现这里保存了刚刚传递的
callback
- 这里实现了两个协议,
_RXKVOObserver
和Disposable
fileprivate final class KVOObserver
: _RXKVOObserver
, Disposable {
typealias Callback = (Any?) -> Void
var retainSelf: KVOObserver?
init(parent: KVOObservableProtocol, callback: @escaping Callback) {
#if TRACE_RESOURCES
_ = Resources.incrementTotal()
#endif
super.init(target: parent.target, retainTarget: parent.retainTarget, keyPath: parent.keyPath, options: parent.options.nsOptions, callback: callback)
self.retainSelf = self
}
override func dispose() {
super.dispose()
self.retainSelf = nil
}
deinit {
#if TRACE_RESOURCES
_ = Resources.decrementTotal()
#endif
}
}
- 点进
_RXKVOObserver
,发现它竟然是一个OC类,说明了RxSwift
的KVO
底层实现利用了OC实现的 - 这里保存了
KVO
的一些相关信息 self.target addObserver
最重要的一步操作是这里进行了观察者的移交操作,把VC的观察功能移交给了当前的内部类
-(instancetype)initWithTarget:(id)target
retainTarget:(BOOL)retainTarget
keyPath:(NSString*)keyPath
options:(NSKeyValueObservingOptions)options
callback:(void (^)(id))callback {
self = [super init];
if (!self) return nil;
self.target = target;
if (retainTarget) {
self.retainedTarget = target;
}
self.keyPath = keyPath;
self.callback = callback;
// 观察者移交 - 中间类
[self.target addObserver:self forKeyPath:self.keyPath options:options context:nil];
return self;
}
- 当外界的
name
值变化后,回来到当前类里的下面这个方法 - 这里执行了外界的传递进来的
callback
方法,并且传递了新的键值对回去
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
@synchronized(self) {
self.callback(change[NSKeyValueChangeNewKey]);
}
- 这个
callback
是在初始化时候的闭包,会流转回去 - 这里通过
observer.on
发送了响应,value
是刚刚传递过来的change[NSKeyValueChangeNewKey]
let observer = KVOObserver(parent: self) { value in
if value as? NSNull != nil {
observer.on(.next(nil))
return
}
observer.on(.next(value as? Element))
}
- 再流转会外界的
subsribe
后的闭包,所以这里能够监听到这个变化,整个流程清清楚楚
.subscribe(onNext: { (change) in
print("observeWeakly订阅到了KVO:\(String(describing: change))")
})
- 那么KVO是怎么销毁监听的呢?
- 在上面还有一个
dispose
没解析,这里调用了super
里的dispose
方法
override func dispose() {
super.dispose()
self.retainSelf = nil
}
- 也就是
_RXKVOObserver
里的销毁dispose
方法,可以看到在这里移除了KVO的观察者,还置nil了当前对象
-(void)dispose {
[self.target removeObserver:self forKeyPath:self.keyPath context:nil];
self.target = nil;
self.retainedTarget = nil;
}
- 所以只要序列销毁,自动调用
dispose
方法,就会销毁我们的KVO观察者
总结
RxSwift
的KVO
底层实现其实是一个中介者模式,通过移交观察者的方式,实现序列的响应和发送效果!