RxSwift #02 | Observables

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<Element> {
    /// 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<Int>.just(one)
}

of

let observable2 = Observable<Int>.of(one, two, three)
let observable3 = Observable<Int>.of([one, two, three])

from

let observable4 = Observable<Int>.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()
}

上述示例代码中,做了以下这些事情:

  1. 定义一个 observable
  2. 订阅一个 observable, 并将返回值 Disposable 类型的对象用一个叫做 subscription 的变量持有
  3. 打印每一个发出的事件
  4. 通过调用 dispose(), 手动取消订阅,此时 observable 不会再发出任何事件。

DisposeBag

每次手动去 dispose 一个订阅是很繁琐的,而且很容易忘记,这样就会导致内存泄漏。

RxSwift 提供了 DisposeBag, 一个 disposeBag 持有了多个 disposable 对象,每个 disposable 对象通过调用 disposed(by:) 方法,将自己添加到 disposeBag 中。在 disposeBag 即将销毁的时候,会一次调用自己持有的每一个 disposable 对象的 dispose() 方法。

let observable = Observable<String>.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)completederror(error)

如果你需要实现一个既可以成功也可以失败的操作,并且可以选择在成功时返回一个值,那么你就可以选择使用 Maybe。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容