为什么我们需要在RxSwift中的“订阅”之后显式调用“ disposeBy(bag)”

时间:2018-10-16 19:59:53

标签: rx-swift

我从博客帖子http://adamborek.com/memory-managment-rxswift/中了解到这一点:

  

当您订阅Observable时,Disposable保留对Observable的引用,而Observable保留对Disposable的强烈引用(Rx创建某种保留周期)。因此,如果用户在导航堆栈中导航,除非您希望将Observable释放,否则将不会被释放。

仅出于理解的目的,我创建了这个虚拟项目:在哪里有一个视图,在视图的中间,有一个巨大的按钮,它将发出有关该按钮被点击多少次的事件。就那么简单。

import UIKit
import RxCocoa
import RxSwift

class Button: UIButton {
    private var number: Int = 0

    private let buttonPushedSubject: PublishSubject<Int> = PublishSubject.init()
    var buttonPushedObservable: Observable<Int> { return buttonPushedSubject }

    deinit {
        print("Button was deallocated.")
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
    }

    @objc final func buttonTapped() {
        number = number + 1
        buttonPushedSubject.onNext(number)
    }
}

class ViewController: UIViewController {
    @IBOutlet private weak var button: Button!

    deinit {
        print("ViewController was deallocated.")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        button.buttonPushedObservable.subscribe(onNext: { (number) in
            print("Number is \(number)")
        }, onError: nil, onCompleted: nil, onDisposed: nil)
    }
}

令人惊讶的是,在关闭此视图控制器后,日志如下所示:

Number is 1
Number is 2
Number is 3
Number is 4
ViewController was deallocated.
Button was deallocated.
...

,这意味着ViewControllerButton已被释放!在这种情况下,我没有调用disposeBy(bag)并且编译器发出警告。

enter image description here

然后,我开始研究subscribe(onNext:...)的实现(以下c / p):

let disposable: Disposable

if let disposed = onDisposed {
    disposable = Disposables.create(with: disposed)
}
else {
    disposable = Disposables.create()
}

let callStack = Hooks.recordCallStackOnError ? Hooks.customCaptureSubscriptionCallstack() : []

let observer = AnonymousObserver<E> { event in

    switch event {
    case .next(let value):
        onNext?(value)
    case .error(let error):
        if let onError = onError {
            onError(error)
        }
        else {
            Hooks.defaultErrorHandler(callStack, error)
        }
        disposable.dispose()
    case .completed:
        onCompleted?()
        disposable.dispose()
    }
}
return Disposables.create(
    self.asObservable().subscribe(observer),
    disposable
)

在上面的代码块中,确实observer通过lambda函数拥有对disposable的强烈引用。但是,我不明白的是,disposable是如何强烈引用observer的?

1 个答案:

答案 0 :(得分:1)

可观察对象处于活动状态时,存在一个参考周期,但是按钮的取消分配会发送一个完整事件,从而中断该周期。

也就是说,如果您执行类似Observable<Int>.interval(3).subscribe()的操作,则流将不会取消分配。

仅在源完成/错误或在生成的一次性对象上调用dispose()时,才流关闭(因此取消分配)流。使用上面的代码行,源(interval)将永远不会完成或出错,并且未保留对一次性对象的引用,因此无法在其上调用dispose()。

想到它的最好方法是... complete / error是源告诉接收器它已经完成发射的方式(这意味着不再需要该流)。并在一次性对象上调用dispose()是一种接收者的方式,它告诉消息源它不希望接收更多事件(这也意味着不再需要该流)。为了取消分配该流,可以源或汇需要报告已完成。

要明确回答您的问题...您不需要将一次性用品添加到处理袋中,但是如果视图控制器删除而不进行处理并且源未发送完整的消息,流将泄漏。因此,为了安全起见,请确保在处理完流后将水槽丢弃。