RxSwift 的整体学习路线

本文最后更新于 2021年4月4日 晚上

这篇是看完了 RxSwift 基础内容后整理出来的学习路线建议.

RxSwift 的学习路线推荐

首先是学习 RxSwift 的基础内容, 包括如下:

  • Observable

  • 特殊的 Observable(Single, Maybe, Completable)

  • Subject: PublishSubject, BehaviorSubject, ReplaySubject, BehaviorReplay.

  • 操作符(过滤, 转换, 合并, 时间操作符等): 主要是能够看到操作符就可以意淫出它对应的 marble diagram.

然后就可以开始如下几块进阶内容:

  • 错误处理

  • Scheduler

  • RxCocoa 基础

而后结合 RxSwift 基础, 就可以开始看源码 + 实战环节了.

Observable

  • of:

  • just:

  • empty: 序列中只包含一个 completed

  • never: 没有任何元素的序列

  • error:

  • create: 使用创建块

  • deferred: 每次向新的观察者提供一个新的序列(它实际是一个 Observable 工厂).

观察者触发序列的构建, 并且直到序列产生了 complete 或 error 才会结束, 在某些情况下观察者不会被主动释放, 故需要入袋或调用 dispose.

三个 Trait

三个 Trait 都是只有且唯一只有一个元素的序列.

  • Single: 使用 create 创建, 只有带有值的成功和错误两种情况, 这个和 promise 比较类似.

  • Completable: 只有完成或错误两种情况.

  • MayBe: 内部要么是不带值的完成,要么是错误, 要么是带有值的成功.

利用 Single 来做的网络请求例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private func requestInfo() -> Single<String> {
if self._session == nil {
let config = URLSessionConfiguration.default
self._session = URLSession(configuration: config)
}
let request = URLRequest(url: URL(string: "https://www.baidu.com")!)
return self._session.rx
.data(request: request)
.map({ data throws -> String in
if let str = String(data: data, encoding: .utf8) { return str }
throw RxError.noElements
})
.asSingle()
}

比起手动搞的的确少了 N 多的代码, 且没有特殊情况可以使用 URLSession 或 RxAlamofire 达到效果.

四个 Subject

  • PublishSubject: Starts empty and only emits new elements to subscribers.

  • BehaviorSubject: Starts with an initial value and replays it or the latest element to new subscribers.

  • ReplaySubject: Initialized with a buffer size and will maintain a buffer of elements up to that size and replay it to new subscribers.

  • Variable: Wraps a BehaviorSubject, preserves its current value as state, and replays only the latest/initial value to new subscribers. 这个操作符在新版本中使用 BehaviorReplay 替代.

主要区别就是看当观察者开始观察后观察的元素是什么样式的, 通过图来看就是:

  • PublishSubject:

    1
    2
    3
    4
    5
    6
    7
    8

    --1--2--3--|--> PublishSubject 序列

    ^--2--3--|--> 第一个观察者观察的序列

    ^--3--|--> 第二个观察者观察的序列

    # 其中 ^ 表示开始观察的点
  • BehaviorSubject:

    1
    2
    3
    4
    5
    6
    7
    8

    --1--2--3--|--> BehaviorSubject 序列

    ^1-2--3--|--> 第一个观察者观察的序列

    ^2-3--|--> 第二个观察者观察的序列

    # 其中 ^ 表示开始观察的点

    即观察者会收到最后一个元素后再收到新的元素.

  • ReplaySubject: 为加强版的 BehaviorSubject, 可以指定缓存大小, 从而让新观察者接收到之前缓存中的内容.

  • BehaviorRelay: 也是一种加强版的 BehaviorSubject, 它除了可以向新观察者提供最后一个元素外, 增加的功能是可以直接通过 value 属性来访问当前序列中最新的值(最后那个值).

实践用法:

  • 比如可以使用 BehaviorRelay 来表示当前用户的登录状态(因为有时外界需要根据登录状态来响应), 或者是直接读取登录状态.

    这里就有一个想法, 是否可以将 app 状态管理中使用若干 Rx 风格的属性, 外界直接观察? 那这个 RxSwift 干的事情没多大区别.

操作符

操作符看起来好像很多, 实际上总结起来也就那么几类, 然后每个类型中取典型的来分析, 后面就在实践中慢慢体会了.

  1. 过滤型:
  • ignoreElements: 忽略全部元素, 其操作结果是生成一个 Completable.

  • elementAt: 从原序列中取第 n 个元素形成一个只包含该元素的新序列.

  • filter: 和 swift 的过滤操作符一致, 形成一个只包含满足条件元素的新序列.

  • skip 族: 包含跳过若干元素, 以及满足条件跳过, 不满足条件跳过. 比如 skip(2),

    • skip: 跳过原序列中指定个数元素后返回新序列.

    • skipUntil: 当另外一个序列发射元素后才根据原序列返回新序列.

    • skipWhile: 根据条件一直跳过, 直到遇到不满足条件的元素才返回新序列.

  • take 族: 和 skip 族的效果正好相反, 通过画图就可以看出来, 它们是一开始就取元素到新序列, 然后在某些情况下终止取.

    • takeWhile: 取满足条件的元素, 直到遇到不满足后停止取.

    • takeUntil: 一直取元素形成新序列, 直到另外一个序列发射元素后停止取.

  • distinctUntilChanged 族: 当然就是通过各种比较, 返回只包含连续不同元素的新序列.

  • throttle: 需要画图来理解, 它是将一个序列中的连续事件间隔小于给定值的元素都放弃, 直到遇到一个元素和后一个元素间隔大于给定值才取.

  1. 变换型:
  • toArray: 将原序列变换为只有一个元素的新序列, 新序列元素是原序列中所有元素组成的数组.

  • map: 经典的 map…

  • enumerated: 和 swift 的一样, 把原序列变换为 index 和 element 的元组.

  • flatMap 族: 对原序列的元素转换为序列然后再平铺(看它的参数就知道了).

    • flatMapLatest: 对原序列中作为元素的内部序列进行转换操作, 并且始终将原序列最后一个元素序列的元素平铺到新序列中. 画图更好理解.
  • materialize: 将原序列的元素重新包装为事件后返回新序列, 相当于对元素加壳, 这样新序列不会因为出现 error 而终止, 因为 error 会变成 Error 事件.

  • dematerialize: 将由事件组成的原序列元素转换为事件对应元素, 相当于对元素剥壳.

这里 flatMap 进行网络请求的简单例子如下:

1
2
3
4
5
6
7
8
guard let url = URL(string: "https://www.baidu.com") else { return }
Observable.just(url)
.map({ return URLRequest(url: $0) })
.flatMap({ URLSession.shared.rx.response(request: $0) })
.filter({ 200..<300 ~= $0.response.statusCode })
.map({ String(data: $0.data, encoding: .utf8) })
.subscribe(onNext: { print($0 ?? "") })
.disposed(by: _disposeBag)

不过对于一个观察者就会产生一次请求, 如果需要多个观察者观察同一个请求, 则需要使用 shared 相关的操作符.

  1. 组合型操作符: 还是包括两大类, 一种是组合多个序列的, 一种是在同一序列中组合元素的.
  • startWith: 将若干元素放到另外一个序列前部并返回新序列

  • concat: 组合两个序列并返回新序列.

  • concatMap: 和 flatMap 不同的是, 这个操作符可以保证内部序列是顺序执行过去的, 比如在网络操作时, 是先第一个 complete, 然后第二个, 依次过去, 保证顺序执行. 而 flatMap 的响应是谁先到就谁先. 这两个操作符都非常有用.

  • merge: 将两个序列中的元素按时间序平铺到新序列上, 即合并, 在用法上要注意:

    1
    2
    let source = Observable.of(left.asObservable(), right.asObservable())
    let observable = source.merge()
  • combinLatest 族: 取所有参与操作的序列中的最后一个元素, 然后组合成一个新序列并返回. 只有全部序列完成后新序列才完成. 且只有全部参与操作的序列有值新序列才会发射值. 用它可以进行同步操作, 比如等待多个请求同时结束后再处理.

  • zip 族: 和 combinLatest 不同的是, 当参与操作的所有序列有一个完成, 新序列就完成.

  • withLatestFrom: 合并两个序列, 并使用参数序列中的最新元素作为新序列的元素, 取元素时是当第一个序列有新元素的情况下才去取. 这个操作符常用于通过一个按钮的点击来取文本框中输入值, 即 点击序列.withLatestFrom(文本框输入值序列).

  • sample: 和 withLatestFrom 不同之处是它只取新的元素. 还是拿按钮和文本框来说, 如果第二次点击时文本框序列没有新元素产生, 则新序列中也不会增加新元素.

  • amb: 参与操作的是两个序列, 该操作符会去判断两个序列谁先发射元素, 然后取消对另外一个序列的关注, 则新序列中的元素只会是最先发射元素的那个序列中的元素. 比如可以使用这个操作符来选取最快的服务器.

  • switchLatest: 参与操作的是多个序列, 这个操作符会自动将观察转移到最后一个发射元素的序列上, 新序列的元素就是由所有序列中最后一次发射元素的序列中的新元素构成的.

下面则是一些在序列中组合元素的操作符:

- `reduce`: 和 Swift 中的类似, 将原序列中的所有元素进行操作后形成只有一个元素的新序列.

- `scan`: 和 Swift 中的类似, 将原序列中的元素顺次和之前的元素进行某种操作后, 将新元素组成新序列.

RxSwift 的整体学习路线
https://blog.rayy.top/2019/01/27/2019-42-rxswift-learning-path/
作者
貘鸣
发布于
2019年1月27日
许可协议