16.3RxSwift中的那些“术语”到底在说什么?

在这一节里,我们先来熟悉一下reactive programming中的常用术语,这些术语表达的大多是我们熟悉的概念,却有一个我们不太熟悉的名字,从某种意义上说,这给我们入门RxSwift也带来了不少麻烦。

从“以时间为索引的常量队列”开始 - Observable

第一个要介绍的,就是我们在之前的例子中提到的“以时间为索引的常量队列”。在RxSwift里,这种概念叫做Observable。在ReactiveX对Observable的说明中,我们可以看到一张这样的图:

16.3RxSwift中的那些“术语”到底在说什么?_第1张图片
common-ideas-about-rx

其中,最上面的一排,就是一个Observable。从左到右,表示时间由远及近的流动过程。上面的每一个形状,就表示在“某个时间点发生的事件”,而最右边的竖线则表示事件成功结束。因此,我们之前过滤用户输入的例子,也可以表示成这样:

common-ideas-about-rx

看到这里,你可能已经有点儿着急了,你说的这些我都明白,代码呢?该怎么写呢?先别急,我们再来看一个概念:Operator

理解operators

在ReactiveX官网可以看到,operators分为两大类:一类用于创建Observable;

16.3RxSwift中的那些“术语”到底在说什么?_第2张图片
common-ideas-about-rx

这些不同的创建方法可以针对不同的事件流类型生成Observable。另一类是接受Observable作为参数,并返回一个新的Observable;

16.3RxSwift中的那些“术语”到底在说什么?_第3张图片
common-ideas-about-rx

它们有点儿类似我们对集合类型使用的各种变形方法,用于在序列中找到我们希望处理的事件,稍后,我们就会看到一个更具体的例子。

创建一个事件序列

接下来,我们先看一些简单的创建Observable的方法。用任何一种之前我们提过的方法创建一个包含RxSwift的项目,这里我们用了SPM。

为了创建一个包含字符1-9的序列,我们可以在Sources/main.swift中添加下面的代码:

import RxSwift

Observable.of("1", "2", "3", "4", "5", "6", "7", "8", "9")

// Or

Observable.from(["1", "2", "3", "4", "5", "6", "7", "8", "9"])

这里,我们就用了两个operator:

  • of:用固定数量的元素生成一个Observable;
  • from:用一个Sequence类型的对象创建一个Observable;

但这两个operator返回的结果是一样的,都是一个包含字符1-9的Observable:

common-ideas-about-rx

对事件序列进行处理

定义好事件序列之后,我们就可以处理过滤偶数的需求了。之前我们提到过,对序列的加工是通过另外一类operator完成的。思路,和我们之前过滤数组是一样的,先把Observable中的元素都变成整数:

_ = Observable.of("1", "2", "3", "4", "5", "6", "7", "8", "9")
        .map { Int($0) }

这里,map就是一个可以对Observable中的元素变形的operator,它返回一个新的Observable对象。因此,我们可以把多个这种加工Observable的方法串联起来。

例如,进一步在变形后的Observable中,找出所有偶数:

_ = Observable.of("1", "2", "3", "4", "5", "6", "7", "8", "9")
    .map { Int($0) }
    .filter { $0 != nil && $0! % 2 == 0 }

这样,filter这个operator返回的,就是一个只包含偶数的Observable了。

尽管上面mapfilter这两个operator和集合中的mapfilter方法非常类似,但它们执行的逻辑却截然不同。

调用集合类型的mapfilter方法,表达的是同步执行的概念,在调用方法的同时,集合就被立即加工处理了。

但我们创建的Observable,表达的是异步操作。Observable中的每一个元素,都可以理解为一个异步发生的事件。因此,当我们对Observable调用mapfilter方法时,只表示我们要对事件序列中的元素进行处理的逻辑,而并不会立即对Observable中的元素进行处理。

为了验证这个结论,我们可以在筛选偶数的时候打印个消息:

_ = Observable.of("1", "2", "3", "4", "5", "6", "7", "8", "9")
    .map { Int($0) }
    .filter {
        if let item = $0, item % 2 == 0 {
            print("Even: \(item)")
            return true
        }

        return false
    }

然后,执行swift build编译,当我们执行编译结果的时候,不会在控制台上打印任何消息。也就是说,我们没有实际执行任何的筛选逻辑。

如何“订阅”事件?

那么,真正的筛选是什么时候发生的呢?答案是,真的有人对这个事件感兴趣的时候。也就是说,有人“订阅”这个Observable中的事件的时候,像这样:

var evenNumberObservable =
    Observable.of("1", "2", "3", "4", "5", "6", "7", "8", "9")
        .map { Int($0) }
        .filter {
            if let item = $0, item % 2 == 0 {
                print("Even: \(item)")
                return true
            }

            return false
        }

evenNumberObservable.subscribe { event in
    print("Event: \(event)")

这表示的,就是我们“从头至尾”关注了evenNumberObservable这个序列中的所有事件。重新编译执行一下,就可以看到筛选的过程和结果了,我们关注到了这个筛选事件中的所有偶数:

16.3RxSwift中的那些“术语”到底在说什么?_第4张图片
common-ideas-about-rx

那么,除了“全程关注”的人之外,还有一类是“半路来的人”,对于这些人,就只能看到他们关注到evenNumberObservable之后,发生的事件了,我们可以用下面的代码,来理解这个场景:

evenNumberObservable.skip(2).subscribe { event in
    print("Event: \(event)")
}

这里,我们用了另外一个operator skip来模拟“半路关注”的情况。skip(2)可以让订阅者“错过”前2次发生的事件。此时,对这个订阅者而言,他就完全不知道之前还过滤出了2个偶数,他看到的结果就是这样的:

16.3RxSwift中的那些“术语”到底在说什么?_第5张图片
common-ideas-about-rx

把这两次订阅放在一个图里,就是这样:

16.3RxSwift中的那些“术语”到底在说什么?_第6张图片
common-ideas-about-rx

通过这两个例子,我们要表达的最重要的一个思想,就是Observable中的每一个元素都表示一个“异步发生的事件”这样的概念,operator对Observable的加工是在订阅的时候发生的。这种只有在订阅的时候才emit事件的Observable,有一个专有的名字,叫做Cold Observable

言外之意,就是也有一种Observable是只要创建了就会自动emit事件的,它们叫做Hot Observable。在后面的内容中,我们会看到这类事件队列的用法。

subscribe也是一个operator

在刚才的例子里,还有一点值得我们注意的就是在结尾,会有一个Event: completed。这就是在我们之前Observable示意图中的竖线,表示这个Observable事件流成功结束了。

实际上,我们使用的subscribe,也是一个operator,用于把事件的订阅者(Observer)和事件的产生者(Observable)关联起来。而Observer和Observable之间,有着下面的约定:

  • 当Observable正常发送事件时,会调用Observer提供的onNext方法,这个过程习惯上叫做emissions
  • 当Observable成功结束时,会调用Observer提供的onCompleted方法;因此,在最后一次调用onNext之后,就会调用onCompleted
  • 当Observable发生错误时,就会调用Observer提供的onError方法,并且,一旦发生错误,就不会再继续发送其它事件了。对于调用onCompleteonNext的过程,习惯上叫做notifications

在RxSwift里,还有一个约定,叫做onDisposed,指的是Observable使用的资源被回收的时候,会调用Observer提供的onDisposed方法。那么,究竟什么是dispose呢?为什么我们需要它?这都要从Observable的类型说起。

理解Observable dispose

一直以来,我们使用的Observable都是有限序列,对evenNumberObservable来说,当它emit了所有的数字之后,就自动结束了,此时,它占用的资源就会被回收,这很好理解。

但事情并不总是如此,有些事件队列是无限的,例如像下面这样:

_ = Observable.interval(1, scheduler: MainScheduler.instance)
    .subscribe(onNext: { print("Subscribed: \($0)") })

dispatchMain()

我们用interval在主线程定义了一个每隔1秒发送一个整数的事件序列。为了让它可以一直emit事件,最后,我们调用了dispatchMain让程序保持不退出。

为了调用dispatchMain,要在文件开始import Foundation

然后,把之前订阅evenNumberObservable的代码注释掉,重新编译执行,就能得到类似下面这样的结果:

16.3RxSwift中的那些“术语”到底在说什么?_第7张图片
common-ideas-about-rx

可以想到,如果我们不强制退出,这会是一个一直执行下去的事件序列。它在程序退出之前,会一直占用着系统资源,在绝大多数时候,这不是我们想要的结果。对于这种无限事件序列,如果我们要在不用的时候回收掉它的资源,就得这样。

例如,假设5秒后,这个事件序列就不再需要了:

public func delay(_ delay: Double,
    closure: @escaping (Void) -> Void) {

    DispatchQueue.main.asyncAfter(
        deadline: .now() + delay) {
            closure()
        }
}

let disposable =
    Observable.interval(1, scheduler: MainScheduler.instance)
        .subscribe(
            onNext: { print("Subscribed: \($0)") },
            onDisposed: { print("The queue was disposed.") })

delay(5) {
    disposable.dispose()
}

在上面的代码里:

  • 首先,我们定义了一个helper function delay,它可以在特定时间之后,执行我们指定的closure;
  • 其次,我们把subscribe的返回值保存在了一个叫做disposable的变量里,我们可以把它理解为是一个订阅对象,我们可以通过这个对象,来取消订阅。同时,我们还指定了onDisposed参数,用来在事件序列被回收时,获得通知;
  • 最后,当我们使用disposable.dispose()的时候,就表示我们要“退订”这个计时事件了。退订后,原来的事件序列就不再继续emit事件了,它占用的资源也会被回收;

编译执行一下,就会看到这样的结果:

16.3RxSwift中的那些“术语”到底在说什么?_第8张图片
common-ideas-about-rx

但通常,这样单独对subscribe的返回值调用dispose()方法并不是一个好的编码习惯。RxSwift提供了一个类似RAII的机制,叫做DisposeBag,我们可以把所有的订阅对象放在一个DisposeBag里,当DisposeBag对象被销毁的时候,它里面“装”的所有订阅对象就会自动取消订阅,对应的事件序列的资源也就被自动回收了。为了理解这个用法,我们可以把之前的代码改成这样:

var bag = DisposeBag()

Observable.interval(1, scheduler: MainScheduler.instance)
    .subscribe(
        onNext: { print("Subscribed: \($0)") },
        onDisposed: { print("The queue was disposed.") })
    .disposed(by: bag)

delay(5) {
    bag = DisposeBag()
}

这次,我们直接串联了subscribe的返回值,调用disposed(by:)方法,把返回的订阅对象直接“装”在了bag里。并且,在delay的closure参数里,我们通过让bag等于一个新的DisposeBag对象,模拟了DisposeBag对象被销毁的场景。编译执行一下,就能看到和之前同样的结果了:

16.3RxSwift中的那些“术语”到底在说什么?_第9张图片
common-ideas-about-rx

以上,就是和Observable dispose相关的概念。

What's next?

看到这里,我们对RxSwift中的基本概念就有了一个比较全面的了解了。什么是Observable,它和普通序列的区别是什么?Operators的功能是什么?什么是Observer,如何订阅各种事件?Dispose回收的究竟是什么,该如何正确使用它?当我们对这些问题的答案成竹在胸的时候,就可以自己在RxSwift的官方Playground中去学习各种相关的细节了。接下来,我们将在一些具体的开发场景里,来体会不同的operators的用法。

你可能感兴趣的:(16.3RxSwift中的那些“术语”到底在说什么?)