rx-swift - 为什么我们需要在 RxSwift 中在 "disposeBy(bag)"之后显式调用 "subscribe"

标签 rx-swift

我是从一篇博文中读到的 http://adamborek.com/memory-managment-rxswift/ :

When you subscribe for an Observable the Disposable keeps a reference to the Observable and the Observable keeps a strong reference to the Disposable (Rx creates some kind of a retain cycle here). Thanks to that if user navigates back in navigation stack the Observable won’t be deallocated unless you want it to be deallocated.

所以纯粹为了理解这一点,我创建了这个虚拟项目:哪里有一个 View ,在 View 的中间,有一个巨大的按钮,它会发出关于按钮被点击多少次的事件。就那么简单。

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

令人惊讶的是,在我关闭这个 View Controller 之后,日志看起来像这样:

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 进行强引用的?

最佳答案

虽然 observable 处于事件状态,但存在一个引用循环,但是按钮的释放会发送一个完整的事件,从而打破循环。

就是说,如果你做这样的事情 Observable<Int>.interval(3).subscribe()流将不会解除分配。

如果源完成/错误或者如果对生成的可释放对象调用 dispose(),Streams 只会关闭(并因此解除分配)。使用上面的代码行,源 ( interval ) 将永远不会完成或出错,并且不会保留对一次性对象的引用,因此无法对其调用 dispose()。

最好的思考方式是……complete/error是源告诉接收器它已完成发射(这意味着不再需要流)并调用 dispose() 的方式。 on the disposable 是 sink 告诉源它不想再接收任何事件的方式(这也意味着不再需要流。)为了释放流,源或接收器需要报告完成了。

要明确回答您的问题...您不需要将一次性元素添加到处置袋中,但是如果 View Controller 在没有处置的情况下删除并且源不发送完整的消息,流将泄漏。所以安全第一,确保水槽在处理完流后进行处置。

关于rx-swift - 为什么我们需要在 RxSwift 中在 "disposeBy(bag)"之后显式调用 "subscribe",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52843198/

相关文章:

ios - 在 RxSwift 中订阅 Observable 或 Driver 花费的时间太长

swift - 为什么 rx.text/rx.observe/rx.etc 不可用 (RxSwift)

swift - 带有 resultSelector 的 RxSwift withLatestFrom 无法编译

ios - rx.sentMessage(#selector(UIViewController.viewDidLoad)) 未触发

ios - 在 RxSwift 的 for 循环中调用多个 API 请求的最佳方式

swift - RxSwift - 如何连接集合的可观察对象

ios - 莫亚 rxswift : Refresh token and restart request

swift - 如何使用 RxSwift 将一个错误映射到另一个错误

swift - API 调用仅使用 RxSwift 执行一次

ios - 无法在网络调用中返回可观察到的 customError