如何取消订阅可观察对象?
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)
}
}
如果我有这样的东西:
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)
}
}