RxSwift KVO observe不返回旧的更改值

时间:2017-02-07 04:33:40

标签: ios swift avfoundation rx-swift

在Rxswift中实现AVFoundation Looping Player example时遇到问题。 在这里的例子是添加观察者代码:

player.addObserver(self, forKeyPath: "status", options: .new, context: &ObserverContexts.playerStatus)
player.addObserver(self, forKeyPath: "currentItem", options: .old, context: &ObserverContexts.currentItem)
player.addObserver(self, forKeyPath: "currentItem.status", options: .new, context: &ObserverContexts.currentItemStatus)

playerAVQueuePlayer

并且观察者回调代码是:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if context == &ObserverContexts.playerStatus {
        guard let newPlayerStatus = change?[.newKey] as? AVPlayerStatus else { return }

        ...

    }
    else if context == &ObserverContexts.currentItem {
        guard let player = player else { return }

        if player.items().isEmpty {
            ...
        }
        else {

            ...

            if let itemRemoved = change?[.oldKey] as? AVPlayerItem {
                ...
            }
        }
    }
    else if context == &ObserverContexts.currentItemStatus {
        guard let newPlayerItemStatus = change?[.newKey] as? AVPlayerItemStatus else { return }

        ...
    }
    else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
    }
}

请注意 currentItem 值的观察者。在这种情况下,我们希望在 currentItem 更改时获得值。这个例子很好。

以下是Rxswift版本中的代码:

player.rx.observe(AVPlayerStatus.self, "status", options: .new)
        .subscribe(onNext: { [weak self] status in
            ...
        }).addDisposableTo(disposeBag)

player.rx.observe(AVPlayerItem.self, "currentItem", options: .old)
        .subscribe(onNext: { [weak self] item in
            guard let `self` = self else { return }
            guard let itemRemoved = item else { return }

            print("\(itemRemoved)")

        }).addDisposableTo(disposeBag)

player.rx.observe(AVPlayerItemStatus.self, "currentItem.status", options: .new)
        .subscribe(onNext: { [weak self] status in
            ...
        }).addDisposableTo(disposeBag)

问题 currentItem 值的观察者总是 nil 。这让我很困惑。为什么无法使用NSKeyValueObservingOptions.old获取旧值。 我试图找出原因。

找到以下代码(在RxCocoa / Foundation / NSObject + Rx.swift中):

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

    var retainSelf: KVOObserver? = nil

    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, callback: callback)
        self.retainSelf = self
    }

    override func dispose() {
        super.dispose()
        self.retainSelf = nil
    }

    deinit {
        #if TRACE_RESOURCES
            _ = Resources.decrementTotal()
        #endif
    }
}

fileprivate class KVOObservable<Element>
    : ObservableType
    , KVOObservableProtocol {
    typealias E = Element?

    unowned var target: AnyObject
    var strongTarget: AnyObject?

    var keyPath: String
    var options: NSKeyValueObservingOptions
    var retainTarget: Bool

    init(object: AnyObject, keyPath: String, options: NSKeyValueObservingOptions, retainTarget: Bool) {
        self.target = object
        self.keyPath = keyPath
        self.options = options
        self.retainTarget = retainTarget
        if retainTarget {
            self.strongTarget = object
        }
    }

    func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == 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)
    }
}

当观察者subscribe时,会创建一个带回调的KVOObserverKVOObserver的基类是_RXKVOObserver

在文件(RxCocoa / Runtime / _RXKVOObserver.m)中,我找到了以下代码:

@implementation _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;
}

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

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

@end

observeValueForKeyPath func中,它只返回change[NSKeyValueChangeNewKey]回调。当我通过NSKeyValueObservingOptions.old键观看currentItem更改时,显然没有任何价值。

我的问题是:我是否采用错误的方式在Rxswift中观察currentItem的旧值?如果是这样,如何在Rxswift中观察currentItem的旧值?

0 个答案:

没有答案