如何取消订阅可观察对象?

How do I unsubscribe from an observable?

如果我有这样的东西:

  func foo() -> Observable<Foo> {
    return Observable.create { observer in
      // ...
    }
  }

  func bar() {
    foo().observeOn(MainScheduler.instance)
      .subscribeNext {
        // ...
      }
      .addDisposableTo(disposeBag)
  }

如果我想在稍后 bar 中从 observable unsubscribe,我该怎么做?

更新

我知道我可以调用 dispose,但是根据 RxSwift docs:

Note that you usually do not want to manually call dispose; this is only educational example. Calling dispose manually is usually a bad code smell.

所以unsubscribe是不是没有实现?我已经深入研究了 RxSwift 代码,就我能理解发生了什么而言,它看起来不像从订阅方法返回的 Disposable 有任何有用的功能(除了处置)。

您将 foo 返回的 Observable 添加到 disposeBag。它在取消分配时处理订阅。 您可以 "manually" 通过调用

释放 disposeBag

disposeBag = nil

在你的某处 class.

问题编辑后:您想有选择地取消订阅某些 Observable,可能是在满足某些条件时。您可以使用另一个代表这些条件的 Observable,并根据需要使用 takeUntil 运算符取消订阅。

//given that cancellingObservable sends `next` value when the subscription to `foo` is no longer needed

foo().observeOn(MainScheduler.instance)
  .takeUntil(cancellingObservable)
  .subscribeNext {
    // ...
  }
  .addDisposableTo(disposeBag)

我所做的是创建另一个 DisposeBag

var tempBag = DisposeBag()

fun bar() {
    foo().subscribe().addDisposable(tempBag)
}

所以当你想处置时,你可以在你想释放时做 tempBag = nil。而你还有另一个 DisposeBag 让其他一次性用品保持活力。

由于上面的回答侧重于取消订阅特定或特定的可观察对象,我将讨论如何正确有效地一起取消订阅所有可观察对象。

如何订阅

this.myVariable$.pipe(takeUntil(this.ngUnsubscribe)).subscribe(value => {
  // Do the thing you want to do after there is change in myVariable
});

如何退订

ngOnDestroy() {
  this.ngUnsubscribe.next();
  this.ngUnsubscribe.complete();
}

使用上面的 ngOnDestroy 您可以取消订阅所有可观察的。

详情

  • Xcode 版本 11.0 (11A420a),iOS 13,Swift 5
  • RxSwift v 5.0.1

解决方案

如果您想取消订阅 Observable,您只需重置 disposeBag

// Way 1
private lazy var disposeBag = DisposeBag()

//...
disposeBag = DisposeBag()

// Way 2
private lazy var disposeBag: DisposeBag? = DisposeBag()

//...
disposeBag = nil

完整样本

import UIKit
import RxSwift

class Service {
    private lazy var publishSubject = BehaviorSubject<Int>(value: count)
    private lazy var count = 0
    var counter: Observable<Int>  { publishSubject }
    func unsubscribeAll() { publishSubject.dispose() }
    func increaseCounter() {
        count += 1
        publishSubject.onNext(count)
    }
}

class ViewController: UIViewController {

    private lazy var service = Service()
    private lazy var disposeBag = DisposeBag()
    private weak var navigationBar: UINavigationBar!

    override func viewDidLoad() {
        super.viewDidLoad()
        setupNavigationBar()
        subscribeToObservables()
    }
}

// MARK: Work with subviews

extension ViewController {

    private func setupNavigationBar() {
        let navigationBar = UINavigationBar()
        view.addSubview(navigationBar)
        navigationBar.translatesAutoresizingMaskIntoConstraints = false
        navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        navigationBar.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
        navigationBar.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true

        let navigationItem = UINavigationItem()
        var barButtons = [UIBarButtonItem]()
        barButtons.append(createNavigationItemButton(title: "subscr.", action: #selector(subscribeButtonTapped)))
        barButtons.append(createNavigationItemButton(title: "unsubscr.", action: #selector(unsubscribeButtonTapped)))
        navigationItem.leftBarButtonItems = barButtons
        navigationItem.rightBarButtonItem = createNavigationItemButton(title: "+", action: #selector(increaseCounterNavigationItemButtonTapped))
        navigationBar.items = [navigationItem]
        self.navigationBar = navigationBar
    }

    private func createNavigationItemButton(title: String, action: Selector?) -> UIBarButtonItem {
        return UIBarButtonItem(title: title, style: .plain, target: self, action: action)
    }

}

// MARK: Work with observers

extension ViewController {
    private func subscribeToObservables() {
        service.counter.subscribe { [weak self] value in
            guard   let value = value.element,
                    let navigationItem = self?.navigationBar?.items?.first else { return }
            navigationItem.title = "Counter \(value)"
            print(value)
        }.disposed(by: disposeBag)

    }

    private func unsubscribeFromObservables() { disposeBag = DisposeBag() }
}

// MARK: Button actions

extension ViewController {
    @objc func increaseCounterNavigationItemButtonTapped(_ source: UIBarButtonItem) { service.increaseCounter() }
    @objc func subscribeButtonTapped(_ source: UIBarButtonItem) { subscribeToObservables() }
    @objc func unsubscribeButtonTapped(_ source: UIBarButtonItem) { unsubscribeFromObservables() }
}

示例应用程序的屏幕截图

您可以用一个新的覆盖 disposeBag。如果您需要其他可观察对象保持活动状态,请创建多个 disposeBags。如果您考虑一下,这就是 class get 被释放时发生的情况 → 它的 disposeBag 被释放,释放 class 拥有的所有订阅。

示例:

注意:这个例子只是展示了原理,我不知道你为什么会像我下面那样专门构建一些东西。也就是说,使用 prepareForReuse 清除单元格可能很有趣,具体取决于您的架构。

class MyClass {

   var clearableDisposeBag = DisposeBag()
   let disposeBag = DisposeBag()

   let dependencies: Dependencies
   let myObservable: MyObservable

   var myOtherObservable: MyOtherObservable?

   init(dependencies: Dependencies) {
      self.dependencies = dependencies
      self.myObservable = dependencies
         .myObservable
         .subscribe(onNext: doSomething)
         .disposed(by: disposeBag)
   }

   private func doSomething() {/*...*/}

   private func doSomethingElse() {/*...*/}

   private func subscribeToChanges() {
      /// clear previous subscription
      clearableDisposeBag = DisposeBag()
      /// subscribe again
      myOtherObservable = dependencies
         .myOtherObservable
         .subscribe(onNext: doSomethingElse)
         .disposed(by: clearableDisposeBag)
   }
}