Disposable - 可被清除的资源

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

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

1. var disposable: Disposable?

3. override func viewWillAppear(_ animated: Bool) {
4. super.viewWillAppear(animated)

6. self.disposable = textField.rx.text.orEmpty
7. .subscribe(onNext: { text in print(text) })
8. }

10. override func viewWillDisappear(_ animated: Bool) {
11. super.viewWillDisappear(animated)

13. self.disposable?.dispose()
14. }

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

DisposeBag - 清除包

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

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

1. var disposeBag = DisposeBag()

3. override func viewWillAppear(_ animated: Bool) {
4. super.viewWillAppear(animated)

6. textField.rx.text.orEmpty
7. .subscribe(onNext: { text in print(text) })
8. .disposed(by: self.disposeBag)
9. }

11. override func viewWillDisappear(_ animated: Bool) {
12. super.viewWillDisappear(animated)

14. self.disposeBag = DisposeBag()
15. }

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

1. var disposeBag = DisposeBag() // 来自父类 ViewController

3. override func viewDidLoad() {
4. super.viewDidLoad()

6. ...

8. usernameValid
9. .bind(to: passwordOutlet.rx.isEnabled)
10. .disposed(by: disposeBag)

12. usernameValid
13. .bind(to: usernameValidOutlet.rx.isHidden)
14. .disposed(by: disposeBag)

16. passwordValid
17. .bind(to: passwordValidOutlet.rx.isHidden)
18. .disposed(by: disposeBag)

20. everythingValid
21. .bind(to: doSomethingOutlet.rx.isEnabled)
22. .disposed(by: disposeBag)

24. doSomethingOutlet.rx.tap
25. .subscribe(onNext: { [weak self] in self?.showAlert() })
26. .disposed(by: disposeBag)
27. }

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

takeUntil

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

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

1. override func viewDidLoad() {
2. super.viewDidLoad()

4. ...

6. _ = usernameValid
7. .takeUntil(self.rx.deallocated)
8. .bind(to: passwordOutlet.rx.isEnabled)

10. _ = usernameValid
11. .takeUntil(self.rx.deallocated)
12. .bind(to: usernameValidOutlet.rx.isHidden)

14. _ = passwordValid
15. .takeUntil(self.rx.deallocated)
16. .bind(to: passwordValidOutlet.rx.isHidden)

18. _ = everythingValid
19. .takeUntil(self.rx.deallocated)
20. .bind(to: doSomethingOutlet.rx.isEnabled)

22. _ = doSomethingOutlet.rx.tap
23. .takeUntil(self.rx.deallocated)
24. .subscribe(onNext: { [weak self] in self?.showAlert() })
25. }

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

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