特性完全是可选的。你可以自由地在程序中的任何地方使用原始的Observable序列,因为所有核心RxSwift / RxCocoa API都支持它们。
特征是如何工作的
特性只是 Observable sequence 的包装器结构。
你可以将它们视为可观察序列的一种构建器模式实现。构建特质后,调用.asObservable() 会将其转换回原始的可观察序列。
1 | struct Single<Element> { |
可观察序列
Observable Sequence: 它是信号源,产生事件,RxSwift 重视 Observable<T>
类。
下面介绍的几个类型都是在 Observable<T>
类的基础上定制化出来的特征序列。
Observable<T>
可发送出来的事件类型
1 | public enum Event<Element> { |
RxSwift 特征 Single/Completable/Maybe
Single
- Single 只能发送两个事件
1 | public enum SingleEvent<Element> { |
- 在 Single 的初始化中可以看到做了 SingleEvent -> Event 的处理
1 | public static func create(subscribe: @escaping (@escaping SingleObserver) -> Disposable) -> Single<Element> { |
- 在
.success
事件中,Single
发送了一个 .next 后紧跟着又发送了 .completed 事件,所以:
- success - 产⽣⼀个单独的元素
- error - 产⽣⼀个错误
- 不会共享附加作用
Single
实际场景:
Single
的例⼦就是执⾏ HTTP 请求,然后返回⼀个应答或错误。不过你也可以⽤ Single 来描述 任何只有⼀个元素的序列。
1 | func getRepo(_ repo: String) -> Single<[String: Any]> { |
订阅:
1 | getRepo("ReactiveX/RxSwift") |
or
1 | getRepo("ReactiveX/RxSwift") |
- 可以对 Observable 调⽤ .asSingle() ⽅法,将它转换为 Single。
Completable
- Completable 也只能发送两个事件,他没有 .next 事件,所以他们有接受数据值相关的事件!
1 | public enum CompletableEvent { |
- Completable 创建方法同 Single,也是做了 CompletableEvent -> Event 的处理
- 没有 next 事件,不会发出元素
- 只发送 completed 或者 error
- 不会共享附加作⽤
Completable
实际场景:
Completable
适⽤于那种你只关⼼任务是否完成,⽽不需要在意任务返回值的情况。它和 Observable
1 | func cacheLocally() -> Completable { |
订阅
1 | cacheLocally() |
or
1 | cacheLocally() |
Maybe
- 它介于 Single 和 Completable 之间,它要么只能发出⼀个元素,要么产⽣⼀个 completed 事件,要么产⽣⼀个 error 事件。
1 | public enum MaybeEvent<Element> { |
- Maybe 初始化方法,做了 MaybeEvent -> Event 的处理
1 | public static func create(subscribe: @escaping (@escaping MaybeObserver) -> Disposable) -> PrimitiveSequence<Trait, Element> { |
使用场景
Observable 调⽤ .asMaybe() ⽅法,将它转换为 Maybe。
RxCocoa 特征 Driver/Signal/ControlEvent
Driver
- 主要是为了简化 UI 层的代码。不过如果你遇到的序 列具有以下特征,你也可以使⽤它:
- 不会产⽣ error 事件
- ⼀定在 MainScheduler 监听(主线程监听)
- 共享附加作⽤
- 为什么要叫 Driver 呢?
它的目的是让 model 数据层驱动 UI 变化。
E.g.
- Drive UI from CoreData model.
- Drive UI using values from other UI elements (bindings). …
初学者会使用这样的:
1 | let results = query.rx.text |
上面的代码会有什么问题?
- 如果 fetchAutoCompleteItems observable sequence 发出 error (eg连接失败,解析错误), 这样的错误不会解绑订阅,UI再也不会响应新的查询
- 如果 fetchAutoCompleteItems 返回的结果在某个后台线程,后台线程返回的结果会绑定到 UI元素,导致确定性的 crashs
- 结果绑定到两个 UI 元素上,意味着每次用户查询都会发送 2次 HTTP 请求,这不是开发者的本意
该代码的更合适版本如下所示:
1 | let results = query.rx.text |
确保在大型系统中正确处理所有需求很具有挑战性,但是有一种更简单的方法,使用编译器和特征来证明满足这些要求。
以下代码:
1 | let results = query.rx.text.asDriver() |
所以上面代码发生了什么?
- asDriver方法将ControlProperty特性转换为Driver特性。
1 | query.rx.text.asDriver() |
注意,没有什么特别的事情要做。
Driver 具有 ControlProperty 特性的所有属性,以及其他一些属性。底层的 observable sequence 只是包装为Driver trait,仅此而已。
- observable sequence 转化为 Driver
1 | .asDriver(onErrorJustReturn: []) |
P.S. 任何 observable sequence 转化为 Driver 都必须满足下面三点
- Can’t error out.
- Observe on main scheduler.
- Sharing side effects (share(replay: 1, scope: .whileConnected)).
那么,如何确保满足这些呢?只需使用普通的Rx运算符即可。asDriver(onErrorJustReturn:[])等效于以下代码。
1 | let safeSequence = xs |
最后一步是使用 drive
,而不是使用 bind(to:)
。
Signal
- Signal 和 Driver 相似,唯⼀的区别是,Driver 会对新观察者回放(重新发送)上⼀个元素,⽽ Signal 不会对新观察者回放上⼀个元素。
- 不会产⽣ error 事件
- 在 MainScheduler 监听(主线程监听)
- 共享附加作⽤
- Signal 和 Driver 的区别示例
1 | let textField: UITextField = ... |
这个例⼦是将⽤户输⼊的姓名绑定到对应的标签上。当⽤户输⼊姓名后,我们创建了⼀个新的观察者,⽤于订阅姓名的字数。
那么问题来了,订阅时,展示字数的标签会⽴即更新吗? 因为 Driver 会对新观察者回放上⼀个元素(当前姓名),所以这⾥是会更新的。在对他进⾏订阅时,标签的默认⽂本会被刷新。这是合理的。
那如果我们⽤ Driver 来描述点击事件呢,这样合理吗?
1 | let button: UIButton = ... |
当⽤户点击⼀个按钮后,创建⼀个新的观察者,来响应点击事件。此时会发⽣什么?
Driver 会把上⼀次的点击事件回放给新观察者。所以,这⾥的 newObserver 在订阅时,就会接受到上次的点击事件,然后弹出提示框。这似乎不太合理。
于是我们就引⼊了 Signal:
1 | ... |
在同样的场景中,Signal 不会把上⼀次的点击事件回放给新观察者,⽽只会将订阅后产⽣的点击事件,发布给新观察者。这正是我们所需要的。
- 结论: ⼀般情况下状态序列我们会选⽤ Driver 这个类型,事件序列我们会选⽤ Signal 这个类型。
ControlProperty
- 它是 Observable / ObservableType 特性,表示UI元素的属性。
值序列仅表示初始控制值和用户启动的值更改。程序化价值的变化将不会报告。
它的特点:
- 永远不会失败
- share(replay: 1)共享最新值
- 它是有状态的,一旦订阅如果有产生过值,那么最新的值会立即重播
- 当控件销毁的时候发送 complete 事件
- 不发送 error 事件
- 在主线程上传播 events
实际使用案例
1 | extension Reactive where Base: UISearchBar { |
1 | extension Reactive where Base: UISegmentedControl { |
ControlEvent
它是 Observable / ObservableType 特性,表示UI元素的事件。
特点:
- 永不失败
- 订阅的时候不会发送初试值
- 当控件销毁时,发送 complete
- 不发送 errors 事件
- 在主线程发送 events
实际使用案例
这是一个典型的示例,你可以在开发中使用它:
1 | public extension Reactive where Base: UIViewController { |
在UICollectionView + Rx中,我们可以通过以下方式找到它:
1 | extension Reactive where Base: UICollectionView { |
引用: