(十八)RxSwift之KVO

872 阅读3分钟

提到KVO,我们立刻就会想到KVO三部曲:

KVO三部曲:

//1.添加观察者
person.addObserver(self, forKeyPath: "name", options: [.old, .new], context: nil)
//2.观察者回调
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    print(change!)
}
//3.移除观察者
deinit {
    person.removeObserver(self, forKeyPath: "name")
}

person

class MPerson: NSObject {
    @objc dynamic var name: String = "马小撂"
}

KVO三部曲使用起来,代码的分布在不同的地方,可读性略低,相比系统的,RXSwift中的KVO就会好很多, 下面我们再来看一下RXSwift中的KVO

RXSwift中的KVO

person.rx.observe(String.self, "name")
    .subscribe(onNext: { (change) in
        print(change!)
    }).disposed(by: disposeBag)
person.rx.observeWeakly(String.self, "name")
    .subscribe(onNext: { (change) in
        print(change!)
    }).disposed(by: disposeBag)

相比于observeobserveWeakly,可以监听weak修饰的属性。

RXSwift中的KVO是如何实现的

第一步 还是序列的创建:

person.rx.observeWeakly(String.self, "name")
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
        }
}
 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)'
}

observeWeaklyKeyPathFor在内部创建了一个类型为KVOObservable的序列propertyObservablefinishWithNilWhenDealloc:当前类释放就不再监听了。

private func observeWeaklyKeyPathFor(
    _ target: NSObject,
    keyPathSections: [String],
    options: KeyValueObservingOptions
    ) -> Observable<AnyObject?> {

    let propertyObservable = KVOObservable(object: target, keyPath: propertyName, options: options.union(.initial), retainTarget: false) as KVOObservable<AnyObject>
    
    return propertyObservable
        .flatMapLatest { (nextTarget: AnyObject?) -> Observable<AnyObject?> in
            // if target is alive, then send change
            // if it's deallocated, don't send anything
    }
}

再来看一下KVOObservable

fileprivate final class KVOObservable<Element>
    : ObservableType
    , KVOObservableProtocol {
    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
        }
    }

    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)
    }
}

KVOObservable初始化保存了一些基本信息,并实现了subscribe函数。

第二步,订阅

订阅时一定会来到subscribe函数,这个函数初始话了一个KVOObserverobserver, 让我们着重的看一下KVOObserver,KVO的观察者

fileprivate final class KVOObserver
    : _RXKVOObserver
    , Disposable {
    typealias Callback = (Any?) -> Void

    var retainSelf: KVOObserver?

    init(parent: KVOObservableProtocol, callback: @escaping Callback) {
        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
    }
}

KVOObserver继承_RXKVOObserver,点进去看一下

-(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;
}

原来,_RXKVOObserver是一个OC的文件,用的是系统的添加观察者的方法。这里面对外界的信息进行了保存,并把观察者设置为我们内部的类,而不是OC的类了。

当属性发生变化时,会走面的这个方法

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    @synchronized(self) {
        self.callback(change[NSKeyValueChangeNewKey]);
    }
}

就会走我们之前保存的self.callback方法,就会来到闭包实现

第三步:发送响应

let observer = KVOObserver(parent: self) { value in
    if value as? NSNull != nil {
        observer.on(.next(nil))
        return
    }
    observer.on(.next(value as? Element))
}

接着发送响应。我们就会订阅到改变的值。 而且在_RXKVOObserver这个类中我们还发现了销毁函数

-(void)dispose {
    [self.target removeObserver:self forKeyPath:self.keyPath context:nil];
    self.target = nil;
    self.retainedTarget = nil;
}

就是说不需要开发者手动移除了。

总体来说RxSwift中的KVO是封装了系统的KVO,将观察者交给自己的内部类,可读性强,而且不需要手动移除观察者。