Everything is a sequence
Observable 是 RxSwift 的核心。
在学习 RxSwift 的过程中,我们经常能够看到 observable, observable sequence, sequence, stream 之类的名词。
需要注意的,在 Rx 的世界中,这些其实都是指的同一个东西,也就是序列,也称为订阅流。
Everything is a sequence.
一个 observable 就是一个 sequence, 事实上,它是异步的,observable 在一段时间内产生事件(event),这被称为发出(emitting)。就比如点击事件,每点击一次按钮,就发出一个点击事件(emitting a tap event)。
订阅流通常用下面这种 marble diagrams 表示,中文直译是“大理石图”:
- 从左到右的箭头表示时间轴
- 带有编号的圆圈表示元素,代表着每一个事件(event),也就是 元素 = 事件。
Lifecycle of an observable
上图中,这个 observable 发出 3 个点击事件,然后结束。最右边的竖线,代表 completed 事件,也意味着整个订阅流的终止(terminated)。
比如:点击一个按钮,发出了 3 个点击事件,然后按钮所在的 view 被 dismiss 掉,发出一个 completed 事件,然后整个订阅流就终止了,被终止的订阅流不能再发出任何事件。
上图中的❌代表着一个 error 事件,和 completed 事件类似,当一个 observable 发出一个 error 事件,这个订阅流就被终止了,不能再发出任何事件。
总结如下:
- 一个 observable 发出 next 事件,next 事件可以携带自定义 element。
- 当终止(terminated)事件发出的时候,也就是 completed 或者 error 事件发出的时候,订阅流不能再发出事件。
/// Represents a sequence event.
///
/// Sequence grammar:
/// **next\\* (error | completed)**
public enum Event {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
Event 本质上是一个 enum, 通过 Swift enum 的能够关联值的特性,实现了 next 事件可以关联 element,以及 error 事件可以关联一个 Swift.Error
Creating observable
RxSwift 中内置了多种创建 observable 的方式:
just
example(of: "just, of, from") {
// 1
let one = 1
let two = 2
let three = 3
// 2
let observable = Observable.just(one)
}
of
let observable2 = Observable.of(one, two, three)
let observable3 = Observable.of([one, two, three])
from
let observable4 = Observable.from([one, two, three])
你可以在你的 playground 中输入上述代码,编译后你会发现,控制台上没有输出任何东西。
因为你还没有通过订阅去做任何事情。
Subscribing to observable
在上一章 RxSwift #01 | 函数式编程和观察者模式 中,我们学习了观察者模式是如何实现的。在 RxSwift 中,subscribe()
其实就是把 observer 添加到 observable 的观察者列表中。
更重要的是,在有 subscriber 之前,observable 是不会发出任何事件的。记住,一个 observable 实际上是一个 sequence 的定义。订阅一个 observable 更像是在 Swift 标准库中调用 Iterator 的 next()
let sequence = 0..<3
var iterator = sequence.makeIterator()
while let n = iterator.next() {
print(n)
}
/* Prints:
0
1
2
*/
之前我们学习了如何定义一个 observable, 下面让我们看看如何订阅一个 observable:
example(of: "subscribe") {
let one = 1
let two = 2
let three = 3
let observable = Observable.of(one, two, three)
observable.subscribe { event in
print(event)
}
}
/**
next(1)
next(2)
next(3)
completed
**/
Disposing and Terminating
记住,一个 observable 直到被订阅之前,不会做任何事。也可以理解为,是订阅触发了 observable 工作,使它发出新的事件,直到 error 或者 completed 事件终止了订阅流。
然而,你可以通过手动取消订阅来终止一个 observable。
example(of: "dispose") {
// 1
let observable = Observable.of("A", "B", "C")
// 2
let subscription = observable.subscribe { event in
// 3
print(event)
}
// 4
subscription.dispose()
}
上述示例代码中,做了以下这些事情:
- 定义一个 observable
- 订阅一个 observable, 并将返回值 Disposable 类型的对象用一个叫做 subscription 的变量持有
- 打印每一个发出的事件
- 通过调用
dispose()
, 手动取消订阅,此时 observable 不会再发出任何事件。
DisposeBag
每次手动去 dispose 一个订阅是很繁琐的,而且很容易忘记,这样就会导致内存泄漏。
RxSwift 提供了 DisposeBag, 一个 disposeBag 持有了多个 disposable 对象,每个 disposable 对象通过调用 disposed(by:)
方法,将自己添加到 disposeBag 中。在 disposeBag 即将销毁的时候,会一次调用自己持有的每一个 disposable 对象的 dispose()
方法。
let observable = Observable.create { observer in
// 1
observer.onNext("1")
// 2
observer.onCompleted()
// 3
observer.onNext("?")
// 4
return Disposables.create()
}
// 这里的最后一步,返回一个 Disposables, 第一次看可能会觉得奇怪
// 记住,subscribe 操作一定要返回一个 Disposables,代表着一个订阅
observable
.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed") }
)
.disposed(by: disposeBag)
/**
output:
1
completed
disposed
**/
如果你忘记了把一个订阅(subscription, also a disposables)添加到 disposeBag, 或者忘记手动调用 dispose 方法,或者没有事件去终止 observable,那么你将可能会导致一个内存泄漏。
Using Traits
在 RxSwift 中,有三种 traits:
- Single
- Completable
- Maybe
正如他们的含义一样——特性,它们拥有不同的特性:
Single
只会发出一个 success(value)
或者一个 error(error)
事件,success(value)
实际上是 next(value)
和一个 completed
事件的组合。
这对一次性过程很有用,这些过程要么成功并产生一个值,要么失败,例如在下载数据或从磁盘加载数据时。
asSingle()
确保你最多只能得到一个元素,如果源序列发出的元素超过一个,则抛出错误。
Completable
只会发出一个 completed
或者一个 error(error)
事件,不会发出任何 next(value)
事件。
当你只关心事件是否成功完成的时候十分有用,比如写入文件。
Maybe
最后,Maybe 是一个 Single 和 Completable 的组合体。它可以发出一个 success(value)
、completed
或 error(error)
。
如果你需要实现一个既可以成功也可以失败的操作,并且可以选择在成功时返回一个值,那么你就可以选择使用 Maybe。