RxSwift学习——特征序列

1,526 阅读7分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

前言

抱歉,最近忙于公司的项目,焦头烂额,所以更新稍微慢了一点。

后面有时间,会把公司项目的一些问题总结一下和大家分享一下。

那么我们接上一节的内容的继续,上一节我将现实中的流水线和Observable(序列)做了比较,通过流水线的工作情况,我们可能更加清晰的认识到序列的工作原理,对于序列的元素发出(next)、异常(error)、完成(completed)都有了认识。

这一节,我们详细介绍一下特征序列,也就是Observable的特异化序列。

特征序列

RxSwift的特征序列分为下面6种:

  • Single
  • Completable
  • Maybe
  • Driver
  • Signal
  • ControlEvent

我会详细介绍与类比Single与Observable的不同,其他的特征序列也就是一举反三的事情了。那么我们开始吧。

Single

Single 是 Observable 的另外一个版本。不像 Observable 可以发出多个元素,它要么只能发出一个元素,要么产生一个 error 事件。

Single与Observable的两个不同点:

  • Observable中可以发出多个元素,而Single只能发出一个元素,你可以理解为:Observable流水线上面一次可以传输多个物品,而Single流水线上面一次只能传输一个物品。

  • Observable可以处理的实现有3个:next、error、completed,而Single只能处理2个事件:success和error。

    • Single的success事件其实就可以理解为Observable的next事件,由于Single一次只传递一个元素完成了,就不会next,而是success啦。

    • 回想一下Single中的success和error,是不是和Swift.Result类型的机制极其相似?

    SingleEvent 的枚举:

    public enum SingleEvent<Element> {
        case success(Element)
        case error(Swift.Error)
    }
    

    然后再回想一下,目前主流的网络请求库Alamofire、网络请求封装库Moya、图片请求库Kingfisher,它们的数据请求返回的类型,是什么?

    Swift.Result类型!

    所以Single也天然的用于异步请求返回中,因为一个网络请求一般只会返回一个结果,要么返回成功,要么返回失败,虽然在成功和失败还有很多需要细化,但是主体不会改变。

    RxMoya也是如此封装的。

    public extension Reactive where Base: MoyaProviderType {
        /// Designated request-making method.
        ///
        /// - Parameters:
        ///   - token: Entity, which provides specifications necessary for a `MoyaProvider`.
        ///   - callbackQueue: Callback queue. If nil - queue from provider initializer will be used.
        /// - Returns: Single response object.
    
        func request(_ token: Base.Target, callbackQueue: DispatchQueue? = nil) -> Single<Response> {
            return Single.create { [weak base] single in
                let cancellableToken = base?.request(token, callbackQueue: callbackQueue, progress: nil) { result in
    
                    switch result {
    
                    case let .success(response):
                        single(.success(response))
                    case let .failure(error):
                        single(.error(error))
                    }
                }
    
                return Disposables.create {
                    cancellableToken?.cancel()
                }
            }
        }
    }
    

有关Single的本质,也可以通过下面的代码一目了然:

  • 首先它是PrimitiveSequence是一个简略写法:
public typealias Single<Element> = PrimitiveSequence<SingleTrait, Element>
  • PrimitiveSequence本质上还是封装了Observable,我们从定义与初始化方法中就可以看出:
/// Observable sequences containing 0 or 1 element.

public struct PrimitiveSequence<Trait, Element> {

    let source: Observable<Element>
    
    init(raw: Observable<Element>) {

        self.source = raw

    }

}

Completable和Maybe

Completable 是 Observable 的另外一个版本。不像 Observable 可以发出多个元素,它要么只能产生一个 completed 事件,要么产生一个 error 事件。

Maybe 是 Observable 的另外一个版本。它介于 Single 和 Completable 之间,它要么只能发出一个元素,要么产生一个 completed 事件,要么产生一个 error 事件。

通过上面的描述,我们可以清晰的看到,Completable、Maybe和Single是多么相似:

  • 它们都只能产生一个单一的事件。

    • Single是success或者error。

      Single在上面已经给出了代码,这里就不重复了。

    • Completable是completed或者error。

    public enum CompletableEvent {
    
        /// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
        case error(Swift.Error)
    
        /// Sequence completed successfully.
        case completed
    }
    
    • Maybe是success或者error或者completed。
    public enum MaybeEvent<Element> {
    
        /// One and only sequence element is produced. (underlying observable sequence emits: `.next(Element)`, `.completed`)
        case success(Element)
    
        /// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
        case error(Swift.Error)
    
        /// Sequence completed successfully.
        case completed
    }
    
  • 它们只是产生事件的类型不同!

  • 它们甚至在源码中别名最后指向的类型都是PrimitiveSequence,只是在后面遵守不同的协议导致了特异化。

Completable:

/// Sequence containing 0 elements

public enum CompletableTrait { }

/// Represents a push style sequence containing 0 elements.

public typealias Completable = PrimitiveSequence<CompletableTrait, Swift.Never>

Maybe:

/// Sequence containing 0 or 1 elements

public enum MaybeTrait { }

/// Represents a push style sequence containing 0 or 1 element.

public typealias Maybe<Element> = PrimitiveSequence<MaybeTrait, Element>

Drive、Signal、ControlEvent

Driver(司机?)  是一个精心准备的特征序列。它主要是为了简化 UI 层的代码。不过如果你遇到的序列具有以下特征,你也可以使用它:

  • 不会产生 error 事件
  • 一定在 MainScheduler 监听(主线程监听)
  • 共享附加作用

Signal 和 Driver 相似,唯一的区别是,Driver对新观察者回放(重新发送)上一个元素,而 Signal 不会对新观察者回放上一个元素。 有如下特性:

  • 不会产生 error 事件
  • 一定在 MainScheduler 监听(主线程监听)
  • 共享附加作用

ControlEvent 专门用于描述 UI 控件所产生的事件,它具有以下特征:

  • 不会产生 error 事件
  • 一定在 MainScheduler 订阅(主线程订阅)
  • 一定在 MainScheduler 监听(主线程监听)
  • 共享附加作用

这里将Drive、Signal、ControlEvent序列放在一起说,看看他们的特性的共同点就知道了:

  • 不会产生 error 事件。

  • 一定在 MainScheduler 监听(主线程监听)。

  • 共享附加作用。

以上种种的特征都说明这些序列非常适合用于驱动UI:

  • 一个UI交互时间不会出现error(除非你的触摸屏坏了,这个硬件损坏,也不是软件的问题),

  • 主线程监听,记得之前写代码的原则吗?一般刷新UI的代码,都需要回到主线程中完成。

  • 至于这个共享附加作用,你可以理解为同一个页面产生的事件,所有的监听者都共享序列事件,而不用一次又一次的反复,这个好难说明呀,直接上官方的例子吧:

let observable: Observable<Teacher> = API.teacher(teacherId: 1)
let shareSideEffects: Driver<Teacher> = observable.asDriver(onErrorDriveWith: .empty())

let observer0: (Teacher) -> () = ...
let observer1: (Teacher) -> () = ...

shareSideEffects.drive(onNext: observer0)
shareSideEffects.drive(onNext: observer1) // 第二次订阅

如果一个序列共享附加作用,那在第二次订阅时,不会重新发起网络请求,而是共享第一次网络请求。

当然Drive、Signal、ControlEvent序列也有不同的地方啦,不然不会划分为3种序列。

ControlEvent序列在它们之中最为简单,你可以认为所以UIControl产生的事件、还有其他UI控件(UIBarButtonItem、UIGestureRecognizer)产生的事件等。

而Driver与Signal序列在差异性上主要在于Driver对新观察者回放(重新发送)上一个元素,而 Signal 不会对新观察者回放上一个元素:

你可以这么理解:

  • Driver发出了元素,A对它进行订阅,那么A会接受到Driver发出的元素,然后B也对它进行了订阅,那么B也会接受到Driver发出的元素。

  • Signal发出了元素,A对它进行订阅,那么A会接受到Signal发出的元素,然后B也对它进行了订阅,那么B也不会接受到Signal发出的元素。

从单词的意思上也可以这么理解:

Driver是一个持续的过程,Driver就是开车嘛,路过的风景都会积累,所以A、B都会接受到Driver发出的元素。

Signal是一个信号,是一个脉冲,Signal是一个瞬间的事情,流星雨只能在定时定点的位置才能看到,A及时赶到,所以看到了流星雨,B来晚了,流星雨错过,肯定看不到了嘛。

一般情况下状态序列我们会选用 Driver] 这个类型,事件序列我们会选用 Signal 这个类型。

需要注意的是Driver和Signal也同一个类的不同别名的表现形式:

public typealias Driver<Element> = SharedSequence<DriverSharingStrategy, Element>
public typealias Signal<Element> = SharedSequence<SignalSharingStrategy, Element>

总结

本节主要讲解了由Observable生成的特征序列。

我们又可以细分为两大块:

由PrimitiveSequence特异化的Single、Completable和Maybe这三种序列,它们每次只能发出一个元素,它们的差异只是事件的类型不同罢了。其中Single很多时候都用于异步网络请求的封装。

由SharedSequence特异化的Driver、Signal序列,以及专门用于表示UI控件产生事件的ControlEvent序列,它们多用于UI驱动。

下一节预告

聊完了Observable,接下来我会讲解Observer,以及更常用的Observable & Observer。

参考文档

RxSwift编写wanandroid客户端现已开源

目前RxSwift编写wanandroid客户端已经开源了——项目链接

我进行了一轮CodeReview,并增加了一些小功能:

  • 添加了iOS的黑暗模式适配

  • 上拉加载更多使用了无感知功能

  • MBProgressHUD替换为SVProgressHUD

记得给个star喔!

附上一张效果图片:

RPReplay_Final1627969241.2021-08-03 13_44_57.gif