Disposable - 可被清除的资源

4.5 Disposable - 可被清除的资源 - 图1

通常来说,一个序列如果发出了 error 或者 completed 事件,那么所有内部资源都会被释放。如果你需要提前释放这些资源或取消订阅的话,那么你可以对返回的 可被清除的资源(Disposable) 调用 dispose 方法:

  1. var disposable: Disposable?
  2. override func viewWillAppear(_ animated: Bool) {
  3. super.viewWillAppear(animated)
  4. self.disposable = textField.rx.text.orEmpty
  5. .subscribe(onNext: { text in print(text) })
  6. }
  7. override func viewWillDisappear(_ animated: Bool) {
  8. super.viewWillDisappear(animated)
  9. self.disposable?.dispose()
  10. }

调用 dispose 方法后,订阅将被取消,并且内部资源都会被释放。通常情况下,你是不需要手动调用 dispose 方法的,这里只是做个演示而已。我们推荐使用 清除包(DisposeBag) 或者 takeUntil 操作符 来管理订阅的生命周期。

DisposeBag - 清除包

4.5 Disposable - 可被清除的资源 - 图2

因为我们用的是 Swift ,所以我们更习惯于使用 ARC 来管理内存。那么我们能不能用 ARC 来管理订阅的生命周期了。答案是肯定了,你可以用 清除包(DisposeBag) 来实现这种订阅管理机制:

  1. var disposeBag = DisposeBag()
  2. override func viewWillAppear(_ animated: Bool) {
  3. super.viewWillAppear(animated)
  4. textField.rx.text.orEmpty
  5. .subscribe(onNext: { text in print(text) })
  6. .disposed(by: self.disposeBag)
  7. }
  8. override func viewWillDisappear(_ animated: Bool) {
  9. super.viewWillDisappear(animated)
  10. self.disposeBag = DisposeBag()
  11. }

清除包 被释放的时候,清除包 内部所有 可被清除的资源(Disposable) 都将被清除。在输入验证中我们也多次看到 清除包 的身影:

  1. var disposeBag = DisposeBag() // 来自父类 ViewController
  2. override func viewDidLoad() {
  3. super.viewDidLoad()
  4. ...
  5. usernameValid
  6. .bind(to: passwordOutlet.rx.isEnabled)
  7. .disposed(by: disposeBag)
  8. usernameValid
  9. .bind(to: usernameValidOutlet.rx.isHidden)
  10. .disposed(by: disposeBag)
  11. passwordValid
  12. .bind(to: passwordValidOutlet.rx.isHidden)
  13. .disposed(by: disposeBag)
  14. everythingValid
  15. .bind(to: doSomethingOutlet.rx.isEnabled)
  16. .disposed(by: disposeBag)
  17. doSomethingOutlet.rx.tap
  18. .subscribe(onNext: { [weak self] in self?.showAlert() })
  19. .disposed(by: disposeBag)
  20. }

这个例子中 disposeBagViewController 具有相同的生命周期。当退出页面时, ViewController 就被释放,disposeBag 也跟着被释放了,那么这里的 5 次绑定(订阅)也就被取消了。这正是我们所需要的。

takeUntil

4.5 Disposable - 可被清除的资源 - 图3

另外一种实现自动取消订阅的方法就是使用 takeUntil 操作符,上面那个输入验证的演示代码也可以通过使用 takeUntil 来实现:

  1. override func viewDidLoad() {
  2. super.viewDidLoad()
  3. ...
  4. _ = usernameValid
  5. .takeUntil(self.rx.deallocated)
  6. .bind(to: passwordOutlet.rx.isEnabled)
  7. _ = usernameValid
  8. .takeUntil(self.rx.deallocated)
  9. .bind(to: usernameValidOutlet.rx.isHidden)
  10. _ = passwordValid
  11. .takeUntil(self.rx.deallocated)
  12. .bind(to: passwordValidOutlet.rx.isHidden)
  13. _ = everythingValid
  14. .takeUntil(self.rx.deallocated)
  15. .bind(to: doSomethingOutlet.rx.isEnabled)
  16. _ = doSomethingOutlet.rx.tap
  17. .takeUntil(self.rx.deallocated)
  18. .subscribe(onNext: { [weak self] in self?.showAlert() })
  19. }

这将使得订阅一直持续到控制器的 dealloc 事件产生为止。

注意⚠️:这里配图中所使用的 Observable 都是“热” Observable,它可以帮助我们理解订阅的生命周期。如果你想要了解 “冷热” Observable 之间的区别,可以参考官方文档 Hot and Cold Observables