RxSwift 簡單理解

RxSwift核心

1. Observable - 可被監(jiān)聽的序列

Observable 可以用于描述元素異步產(chǎn)生的序列傲茄。這樣我們生活中許多事物都可以通過它來表示: Observable<Double> 溫度
例如:你可以將溫度看作是一個(gè)序列劈猿,然后監(jiān)測這個(gè)溫度值,最后對這個(gè)值做出響應(yīng)庸蔼。例如:當(dāng)室溫高于 33 度時(shí),打開空調(diào)降溫

特征序列
  • Single

Single 是 Observable 的另外一個(gè)版本贮匕。不像 Observable 可以發(fā)出多個(gè)元素姐仅,它要么只能發(fā)出一個(gè)元素,要么產(chǎn)生一個(gè) error 事件刻盐。
A.發(fā)出一個(gè)元素掏膏,或一個(gè) error 事件
B.不會共享狀態(tài)變化

  • Completable

Completable 是 Observable 的另外一個(gè)版本。不像 Observable 可以發(fā)出多個(gè)元素敦锌,它要么只能產(chǎn)生一個(gè) completed 事件馒疹,要么產(chǎn)生一個(gè) error 事件。
A.發(fā)出零個(gè)元素
B.發(fā)出一個(gè) completed 事件或者一個(gè) error 事件
C.不會共享狀態(tài)變化

  • Maybe

Maybe 是 Observable 的另外一個(gè)版本乙墙。它介于 Single 和 Completable 之間颖变,它要么只能發(fā)出一個(gè)元素生均,要么產(chǎn)生一個(gè) completed 事件,要么產(chǎn)生一個(gè) error 事件悼做。
A.發(fā)出一個(gè)元素或者一個(gè) completed 事件或者一個(gè) error 事件
B.不會共享狀態(tài)變化”

  • Driver

Driver(司機(jī)疯特?) 是一個(gè)精心準(zhǔn)備的特征序列。它主要是為了簡化 UI 層的代碼肛走。不過如果你遇到的序列具有以下特征漓雅,你也可以使用它:
A.不會產(chǎn)生 error 事件
B.一定在 MainScheduler 監(jiān)聽(主線程監(jiān)聽)
C.共享狀態(tài)變化

*ControlEvent

ControlEvent 專門用于描述 UI 控件所產(chǎn)生的事件,它具有以下特征:
A.不會產(chǎn)生 error 事件
B.一定在 MainScheduler 訂閱(主線程訂閱)
C.一定在 MainScheduler 監(jiān)聽(主線程監(jiān)聽)
D.共享狀態(tài)變化

2. Observer - 觀察者

觀察者 是用來監(jiān)聽事件朽色,然后它需要這個(gè)事件做出響應(yīng)邻吞。例如:彈出提示框就是觀察者,它對點(diǎn)擊按鈕這個(gè)事件做出響應(yīng)葫男。

例如:當(dāng)室溫高于 33 度時(shí)抱冷,打開空調(diào)降溫
打開空調(diào)降溫就是觀察者 Observer<Double>。

創(chuàng)建觀察者:
創(chuàng)建觀察者最直接的方法就是在 Observable 的 subscribe 方法后面描述梢褐,事件發(fā)生時(shí)旺遮,需要如何做出響應(yīng)。而觀察者就是由后面的 onNext盈咳,onError耿眉,onCompleted的這些閉包構(gòu)建出來的。

特征觀察者
  • AnyObserver

AnyObserver 可以用來描敘任意一種觀察者

*Binder

Binder 主要有以下兩個(gè)特征:
A.不會處理錯(cuò)誤事件
B.確保綁定都是在給定 Scheduler 上執(zhí)行(默認(rèn) MainScheduler)
一旦產(chǎn)生錯(cuò)誤事件鱼响,在調(diào)試環(huán)境下將執(zhí)行 fatalError鸣剪,在發(fā)布環(huán)境下將打印錯(cuò)誤信息。

let observer: AnyObserver<Bool> = AnyObserver { [weak self] (event) in
    switch event {
    case .next(let isHidden):
        self?.usernameValidOutlet.isHidden = isHidden
    default:
        break
    }
}

//observer
usernameValid.bind(to: observer).disposed(by: disposeBag)
extension Reactive where Base: UIView {
  public var isHidden: Binder<Bool> {
      return Binder(self.base) { view, hidden in
          view.isHidden = hidden
      }
  }
}

usernameValid.bind(to: usernameValidOutlet.rx.isHidden).disposed(by: disposeBag)

3. Observable & Observer 既是可被監(jiān)聽的序列也是觀察者

在我們所遇到的事物中丈积,有一部分非常特別筐骇。它們既是可被監(jiān)聽的序列也是觀察者。
例如:textField的當(dāng)前文本江滨。它可以看成是由用戶輸入铛纬,而產(chǎn)生的一個(gè)文本序列。也可以是由外部文本序列唬滑,來控制當(dāng)前顯示內(nèi)容的觀察者:

// 作為可被監(jiān)聽的序列
let observable = textField.rx.text
observable.subscribe(onNext: { text in show(text: text) })
// 作為觀察者
let observer = textField.rx.text
let text: Observable<String?> = ...
text.bind(to: observer)
Subject

*AsyncSubject

AsyncSubject 將在源 Observable 產(chǎn)生完成事件后饺鹃,發(fā)出最后一個(gè)元素(僅僅只有最后一個(gè)元素),如果源 Observable 沒有發(fā)出任何元素间雀,只有一個(gè)完成事件悔详。那 AsyncSubject 也只有一個(gè)完成事件。
它會對隨后的觀察者發(fā)出最終元素惹挟。如果源 Observable 因?yàn)楫a(chǎn)生了一個(gè) error 事件而中止茄螃, AsyncSubject 就不會發(fā)出任何元素,而是將這個(gè) error 事件發(fā)送出來连锯。

AsyncSubject.png
AsyncSubject.png
let disposeBag = DisposeBag()
let subject = AsyncSubject<String>()

subject
  .subscribe { print("Subscription: 1 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("??")
subject.onNext("??")
subject.onNext("??")
subject.onCompleted()

輸出結(jié)果:
Subscription: 1 Event: next(??)
Subscription: 1 Event: completed

*PublishSubject

PublishSubject 將對觀察者發(fā)送訂閱后產(chǎn)生的元素归苍,而在訂閱前發(fā)出的元素將不會發(fā)送給觀察者用狱。如果你希望觀察者接收到所有的元素,你可以通過使用Observable 的 create 方法來創(chuàng)建 Observable拼弃,或者使用 ReplaySubject夏伊。
如果源 Observable 因?yàn)楫a(chǎn)生了一個(gè) error 事件而中止, PublishSubject 就不會發(fā)出任何元素吻氧,而是將這個(gè) error 事件發(fā)送出來溺忧。

PublishSubject.png
PublishSubject.png
let disposeBag = DisposeBag()
let subject = PublishSubject<String>()

subject
  .subscribe { print("Subscription: 1 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("??")
subject.onNext("??")

subject
  .subscribe { print("Subscription: 2 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("???")
subject.onNext("???")

輸出結(jié)果:
Subscription: 1 Event: next(??)
Subscription: 1 Event: next(??)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)

*ReplaySubject

ReplaySubject 將對觀察者發(fā)送全部的元素,無論觀察者是何時(shí)進(jìn)行訂閱的盯孙。
這里存在多個(gè)版本的 ReplaySubject鲁森,有的只會將最新的 n 個(gè)元素發(fā)送給觀察者,有的只會將限制時(shí)間段內(nèi)最新的元素發(fā)送給觀察者振惰。
如果把 ReplaySubject 當(dāng)作觀察者來使用歌溉,注意不要在多個(gè)線程調(diào)用 onNext, onError 或 onCompleted。這樣會導(dǎo)致無序調(diào)用骑晶,將造成意想不到的結(jié)果痛垛。

ReplaySubject.png
let disposeBag = DisposeBag()
let subject = ReplaySubject<String>.create(bufferSize: 1)

subject
  .subscribe { print("Subscription: 1 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("??")
subject.onNext("??")

subject
  .subscribe { print("Subscription: 2 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("???")
subject.onNext("???")

輸出結(jié)果:
Subscription: 1 Event: next(??)
Subscription: 1 Event: next(??)
Subscription: 2 Event: next(??)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)

*BehaviorSubject

當(dāng)觀察者對 BehaviorSubject 進(jìn)行訂閱時(shí),它會將源 Observable 中最新的元素發(fā)送出來(如果不存在最新的元素桶蛔,就發(fā)出默認(rèn)元素)匙头。然后將隨后產(chǎn)生的元素發(fā)送出來。如果源 Observable 因?yàn)楫a(chǎn)生了一個(gè) error 事件而中止羽圃, BehaviorSubject 就不會發(fā)出任何元素,而是將這個(gè) error 事件發(fā)送出來

98C97A0E-D2A0-4C84-964D-90A2BF88B2EC.png
413FA601-5878-433B-BDC1-FA57A4196385.png
let disposeBag = DisposeBag()
let subject = BehaviorSubject(value: "??")

subject
  .subscribe { print("Subscription: 1 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("??")
subject.onNext("??")

subject
  .subscribe { print("Subscription: 2 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("???")
subject.onNext("???")

subject
  .subscribe { print("Subscription: 3 Event:", $0) }
  .disposed(by: disposeBag)

subject.onNext("??")
subject.onNext("??")

輸出結(jié)果:
Subscription: 1 Event: next(??)
Subscription: 1 Event: next(??)
Subscription: 1 Event: next(??)
Subscription: 2 Event: next(??)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)
Subscription: 3 Event: next(???)
Subscription: 1 Event: next(??)
Subscription: 2 Event: next(??)
Subscription: 3 Event: next(??)
Subscription: 1 Event: next(??)
Subscription: 2 Event: next(??)
Subscription: 3 Event: next(??)
Variable

在 Swift 中我們經(jīng)常會用 var 關(guān)鍵字來聲明變量抖剿。RxSwift 提供的 Variable 實(shí)際上是 var 的 Rx 版本朽寞,你可以將它看作是 RxVar。
我們來對比一下 var 以及 Variable 的用法:

使用 var:

// 在 ViewController 中
var model: Model? = nil {
    didSet { updateUI(with: model) }
func updateUI(with model: Model?) { ... }
let model: Variable<Model?> = Variable(nil)

override func viewDidLoad() {
    super.viewDidLoad()

    model.asObservable()
        .subscribe(onNext: { [weak self] model in
            self?.updateUI(with: model)
        })
        .disposed(by: disposeBag)

func updateUI(with model: Model?) { ... }
func getModel() -> Model { ... }

第一種使用 var 的方式十分常見斩郎,在 ViewController 中監(jiān)聽 Model 的變化脑融,然后刷新頁面。
第二種使用 Variable 則是 RxSwift 獨(dú)有的缩宜。Variable 幾乎提供了 var 的所有功能肘迎。另外,加上一條非常重要的特性锻煌,就是可以通過調(diào)用 asObservable() 方法轉(zhuǎn)換成序列妓布。然后你可以對這個(gè)序列應(yīng)用操作符,來合成其他的序列宋梧。所以匣沼,如果我們聲明的變量需要提供 Rx 支持,那就選用 Variable 這個(gè)類型捂龄。

說明
Variable 封裝了一個(gè) BehaviorSubject释涛,所以它會持有當(dāng)前值加叁,并且 Variable 會對新的觀察者發(fā)送當(dāng)前值。它不會產(chǎn)生 error 事件唇撬。Variable 在 deinit 時(shí)它匕,會發(fā)出一個(gè) completed 事件

Operator - 操作符
6800A280-ADA2-4F79-A87B-11891FCF0158.png

操作符可以幫助大家創(chuàng)建新的序列,或者變化組合原有的序列窖认,從而生成一個(gè)新的序列豫柬。
我們之前在輸入驗(yàn)證例子中就多次運(yùn)用到操作符。例如耀态,通過 map 方法將輸入的用戶名轮傍,轉(zhuǎn)換為用戶名是否有效。然后用這個(gè)轉(zhuǎn)化后來的序列來控制紅色提示語是否隱藏首装。我們還通過 combineLatest 方法创夜,將用戶名是否有效和密碼是否有效合并成兩者是否同時(shí)有效。然后用這個(gè)合成后來的序列來控制按鈕是否可點(diǎn)擊仙逻。
這里 map 和 combineLatest 都是操作符驰吓,它們可以幫助我們構(gòu)建所需要的序列。現(xiàn)在系奉,我們再來看幾個(gè)例子:

Filter:


99EE92D6-D016-4605-BB95-BA1FF5BAD869.png

Map:


D382E434-EC82-438B-B50A-A4297FFA708E.png

5. Disposable - 可被清除的資源

Disposable - 可被清除的資源

通常來說圈盔,一個(gè)序列如果發(fā)出了 error 或者 completed 事件匪蝙,那么所有內(nèi)部資源都會被釋放。如果你需要提前釋放這些資源或取消訂閱的話,那么你可以對返回的 可被清除的資源(Disposable) 調(diào)用 dispose 方法:

F5A2FE7F-5526-4347-A7AB-80A37D135B12.png
var disposable: Disposable?
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.disposable = textField.rx.text.orEmpty
        .subscribe(onNext: { text in print(text) })
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    self.disposable?.dispose()
}

調(diào)用 dispose 方法后沿腰,訂閱將被取消,并且內(nèi)部資源都會被釋放厌漂。通常情況下槽地,你是不需要手動調(diào)用 dispose 方法的。我們推薦使用 清除包(DisposeBag) 或者 takeUntil 操作符 來管理訂閱的生命周期并鸵。

DisposeBag - 清除包

因?yàn)槲覀冇玫氖?Swift 鸳粉,所以我們更習(xí)慣于使用 ARC 來管理內(nèi)存。那么我們能不能用 ARC 來管理訂閱的生命周期了园担。答案是肯定了届谈,你可以用 清除包(DisposeBag) 來實(shí)現(xiàn)這種訂閱管理機(jī)制

404F9871-3ED0-4063-993F-3545B78FD9FD.png
var disposeBag = DisposeBag()

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    textField.rx.text.orEmpty
        .subscribe(onNext: { text in print(text) })
        .disposed(by: self.disposeBag)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    self.disposeBag = DisposeBag()
}

當(dāng) 清除包 被釋放的時(shí)候,清除包 內(nèi)部所有 可被清除的資源(Disposable) 都將被清除弯汰。

takeUntil
8E12A1D5-9ED6-4018-86A0-DC6D5FB1CF2F.png

另外一種實(shí)現(xiàn)自動取消訂閱的方法就是使用 takeUntil 操作符艰山,上面那個(gè)輸入驗(yàn)證的演示代碼也可以通過使用 takeUntil 來實(shí)現(xiàn):

override func viewDidLoad() {
“super.viewDidLoad()

    ...

    _ = usernameValid
        .takeUntil(self.rx.deallocated)
        .bind(to: passwordOutlet.rx.isEnabled)

    _ = usernameValid
        .takeUntil(self.rx.deallocated)
        .bind(to: usernameValidOutlet.rx.isHidden)

    _ = passwordValid
        .takeUntil(self.rx.deallocated)
        .bind(to: passwordValidOutlet.rx.isHidden)

    _ = everythingValid
        .takeUntil(self.rx.deallocated)
        .bind(to: doSomethingOutlet.rx.isEnabled)
}

這將使得訂閱一直持續(xù)到控制器的 dealloc 事件產(chǎn)生為止。

6. Schedulers - 調(diào)度器

13FA831F-BA09-4DF5-9D93-2385BF472352.png

Schedulers 是 Rx 實(shí)現(xiàn)多線程的核心模塊咏闪,它主要用于控制任務(wù)在哪個(gè)線程或隊(duì)列運(yùn)行程剥。

let rxData: Observable<Data> = ...

rxData.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
    .observeOn(MainScheduler.instance)
    .subscribe(onNext: { [weak self] data in
        self?.data = data
    })
    .disposed(by: disposeBag)

使用 subscribeOn

我們用 subscribeOn 來決定數(shù)據(jù)序列的構(gòu)建函數(shù)在哪個(gè) Scheduler 上運(yùn)行。以上例子中,由于獲取 Data 需要花很長的時(shí)間织鲸,所以用 subscribeOn 切換到 后臺 Scheduler 來獲取 Data舔腾。這樣可以避免主線程被阻塞。

使用 observeOn

我們用 observeOn 來決定在哪個(gè) Scheduler 監(jiān)聽這個(gè)數(shù)據(jù)序列搂擦。以上例子中稳诚,通過使用 observeOn 方法切換到主線程來監(jiān)聽并且處理結(jié)果。
一個(gè)比較典型的例子就是瀑踢,在后臺發(fā)起網(wǎng)絡(luò)請求扳还,然后解析數(shù)據(jù),最后在主線程刷新頁面橱夭。你就可以先用 subscribeOn 切到后臺去發(fā)送請求并解析數(shù)據(jù)氨距,最后用 observeOn 切換到主線程更新頁面。

MainScheduler

MainScheduler 代表主線程棘劣。如果你需要執(zhí)行一些和 UI 相關(guān)的任務(wù)俏让,就需要切換到該 Scheduler 運(yùn)行。

SerialDispatchQueueScheduler

SerialDispatchQueueScheduler 抽象了竄行 DispatchQueue茬暇。如果你需要執(zhí)行一些竄行任務(wù)首昔,可以切換到這個(gè) Scheduler 運(yùn)行。

ConcurrentDispatchQueueScheduler

ConcurrentDispatchQueueScheduler 抽象了并行 DispatchQueue糙俗。如果你需要執(zhí)行一些并發(fā)任務(wù)勒奇,可以切換到這個(gè) Scheduler 運(yùn)行。

OperationQueueScheduler

OperationQueueScheduler 抽象了 NSOperationQueue巧骚。
它具備 NSOperationQueue 的一些特點(diǎn)赊颠,例如,你可以通過設(shè)置 maxConcurrentOperationCount劈彪,來控制同時(shí)執(zhí)行并發(fā)任務(wù)的最大數(shù)量竣蹦。

7. Error Handling - 錯(cuò)誤處理

一旦序列里面產(chǎn)出了一個(gè) error 事件,整個(gè)序列將被終止粉臊。RxSwift 主要有兩種錯(cuò)誤處理機(jī)制:

  • retry - 重試
  • catch - 恢復(fù)
retry - 重試

retry 可以讓序列在發(fā)生錯(cuò)誤后重試:

// 請求 JSON 失敗時(shí)草添,立即重試驶兜,
// 重試 3 次后仍然失敗扼仲,就將錯(cuò)誤拋出

let rxJson: Observable<JSON> = ...

rxJson
    .retry(3)
    .subscribe(onNext: { json in
        print("取得 JSON 成功: \(json)")
    }, onError: { error in
        print("取得 JSON 失敗: \(error)")
})
    .disposed(by: disposeBag)

以上的代碼非常直接 retry(3) 就是當(dāng)發(fā)生錯(cuò)誤時(shí),就進(jìn)行重試操作抄淑,并且最多重試 3 次

retryWhen

如果我們需要在發(fā)生錯(cuò)誤時(shí)屠凶,經(jīng)過一段延時(shí)后重試,那可以這樣實(shí)現(xiàn):

let retryDelay: Double = 5  // 重試延時(shí) 5 秒

rxJson
    .retryWhen { (rxError: Observable<Error>) -> Observable<Int> in
        return Observable.timer(retryDelay, scheduler: MainScheduler.instance)
    }
    .subscribe(...)
    .disposed(by: disposeBag)

這里我們需要用到 retryWhen 操作符肆资,這個(gè)操作符主要描述應(yīng)該在何時(shí)重試矗愧,并且通過閉包里面返回的 Observable 來控制重試的時(shí)機(jī):

閉包里面的參數(shù)是 Observable<Error> 也就是所產(chǎn)生錯(cuò)誤的序列,然后返回值是一個(gè) Observable。當(dāng)這個(gè)返回的Observable 發(fā)出一個(gè)元素時(shí)唉韭,就進(jìn)行重試操作夜涕。當(dāng)它發(fā)出一個(gè) error 或者 completed 事件時(shí),就不會重試属愤,并且將這個(gè)事件傳遞給到后面的觀察者女器。

如果需要加上一個(gè)最大重試次數(shù)的限制:

// 請求 JSON 失敗時(shí),等待 5 秒后重試住诸,
// 重試 4 次后仍然失敗驾胆,就將錯(cuò)誤拋出

let maxRetryCount = 4       // 最多重試 4 次
let retryDelay: Double = 5  // 重試延時(shí) 5 秒

rxJson
    .retryWhen { (rxError: Observable<Error>) -> Observable<Int> in
        return rxError.flatMapWithIndex { (error, index) -> Observable<Int> in
            guard index < maxRetryCount else {
                return Observable.error(error)
            }
        return Observable<Int>.timer(retryDelay, scheduler: MainScheduler.instance)
        }
    }
    .subscribe(...)
    .disposed(by: disposeBag)

我們用 flatMapWithIndex 這個(gè)操作符,因?yàn)樗梢越o我們提供錯(cuò)誤的索引數(shù) index贱呐。然后用這個(gè)索引數(shù)判斷是否超過最大重試數(shù)丧诺,如果超過了,就將錯(cuò)誤拋出奄薇。如果沒有超過驳阎,就等待 5 秒后重試。

catchError - 恢復(fù)

catchError 可以在錯(cuò)誤產(chǎn)生時(shí)惕艳,用一個(gè)備用元素或者一組備用元素將錯(cuò)誤替換掉:

searchBar.rx.text.orEmpty
    ...
    .flatMapLatest { query -> Observable<[Repository]> in
        ...
        return searchGitHub(query)
            .catchErrorJustReturn([])
    }
    ...
    .bind(to: ...)
    .disposed(by: disposeBag)

我們開頭的 Github 搜索就用到了catchErrorJustReturn搞隐。當(dāng)錯(cuò)誤產(chǎn)生時(shí),就返回一個(gè)空數(shù)組远搪,于是就會顯示一個(gè)空列表頁劣纲。

你也可以使用 catchError,當(dāng)錯(cuò)誤產(chǎn)生時(shí)谁鳍,將錯(cuò)誤事件替換成一個(gè)備選序列:

// 先從網(wǎng)絡(luò)獲取數(shù)據(jù)癞季,如果獲取失敗了,就從本地緩存獲取數(shù)據(jù)

let rxData: Observable<Data> = ...      // 網(wǎng)絡(luò)請求的數(shù)據(jù)
let cahcedData: Observable<Data> = ...  // 之前本地緩存的數(shù)據(jù)

rxData
    .catchError { _ in cahcedData }
    .subscribe(onNext: { date in
        print("獲取數(shù)據(jù)成功: \(date.count)")
    })
    .disposed(by: disposeBag)

常用操作符

  • catchError
    從一個(gè)錯(cuò)誤事件中恢復(fù)倘潜,將錯(cuò)誤事件替換成一個(gè)備選序列


    catchError

    catchError 操作符將會攔截一個(gè) error 事件绷柒,將它替換成其他的元素或者一組元素,然后傳遞給觀察者涮因。這樣可以使得 Observable 正常結(jié)束废睦,或者根本都不需要結(jié)束。

let disposeBag = DisposeBag()

let sequenceThatFails = PublishSubject<String>()
let recoverySequence = PublishSubject<String>()

sequenceThatFails
    .catchError {
        print("Error:", $0)
        return recoverySequence
    }
    .subscribe { print($0) }
    .disposed(by: disposeBag)

sequenceThatFails.onNext("??")
sequenceThatFails.onNext("??")
sequenceThatFails.onNext("??")
sequenceThatFails.onNext("??")
sequenceThatFails.onError(TestError.test)

recoverySequence.onNext("??")

輸出結(jié)果:
next(??)
next(??)
next(??)
next(??)
Error: test
next(??)

  • catchErrorJustReturn
    catchErrorJustReturn 操作符會將error 事件替換成其他的一個(gè)元素养泡,然后結(jié)束該序列嗜湃。
let disposeBag = DisposeBag()
let sequenceThatFails = PublishSubject<String>()

sequenceThatFails
    .catchErrorJustReturn("??")
    .subscribe { print($0) }
    .disposed(by: disposeBag)

sequenceThatFails.onNext("??")
sequenceThatFails.onNext("??")
sequenceThatFails.onNext("??")
sequenceThatFails.onNext("??")
sequenceThatFails.onError(TestError.test)

輸出結(jié)果:
next(??)
next(??)
next(??)
next(??)
next(??)
completed
  • combineLatest
    當(dāng)多個(gè) Observables 中任何一個(gè)發(fā)出一個(gè)元素,就發(fā)出一個(gè)元素澜掩。這個(gè)元素是由這些 Observables 中最新的元素购披,通過一個(gè)函數(shù)組合起來的
combineLatest

combineLatest 操作符將多個(gè) Observables 中最新的元素通過一個(gè)函數(shù)組合起來,然后將這個(gè)組合的結(jié)果發(fā)出來肩榕。這些源 Observables 中任何一個(gè)發(fā)出一個(gè)元素刚陡,他都會發(fā)出一個(gè)元素(前提是,這些 Observables 曾經(jīng)發(fā)出過元素)。

let disposeBag = DisposeBag()
let first = PublishSubject<String>()
let second = PublishSubject<String>()

Observable.combineLatest(first, second) { $0 + $1 }
          .subscribe(onNext: { print($0) })
          .disposed(by: disposeBag)

first.onNext("1")
second.onNext("A")
first.onNext("2")
second.onNext("B")
second.onNext("C")
second.onNext("D")
first.onNext("3")
first.onNext("4")

輸出結(jié)果:
1A
2A
2B
2C
2D
3D
4D
  • concat
    讓兩個(gè)或多個(gè) Observables 按順序串連起來
concat

concat 操作符將多個(gè) Observables 按順序串聯(lián)起來筐乳,當(dāng)前一個(gè) Observable 元素發(fā)送完畢后歌殃,后一個(gè) Observable 才可以開始發(fā)出元素。

concat 將等待前一個(gè) Observable 產(chǎn)生完成事件后蝙云,才對后一個(gè) Observable 進(jìn)行訂閱挺份。如果后一個(gè)是“熱” Observable ,在它前一個(gè) Observable 產(chǎn)生完成事件前贮懈,所產(chǎn)生的元素將不會被發(fā)送出來匀泊。

startWith 和它十分相似。但是startWith不是在后面添加元素朵你,而是在前面插入元素各聘。

merge 和它也是十分相似。merge并不是將多個(gè) Observables 按順序串聯(lián)起來抡医,而是將他們合并到一起躲因,不需要 Observables 按先后順序發(fā)出元素。

let disposeBag = DisposeBag()

let subject1 = BehaviorSubject(value: "??")
let subject2 = BehaviorSubject(value: "??")

let variable = Variable(subject1)

variable.asObservable()
        .concat()
        .subscribe { print($0)
}.disposed(by: disposeBag)

subject1.onNext("??")
subject1.onNext("??")

variable.value = subject2

subject2.onNext("I would be ignored")
subject2.onNext("??")

subject1.onCompleted()

subject2.onNext("??")

輸出結(jié)果:
next(??)
next(??)
next(??)
next(??)
next(??)
  • concatMap
    將 Observable 的元素轉(zhuǎn)換成其他的 Observable忌傻,然后將這些 Observables 串連起來
concatMap

concatMap 操作符將源 Observable 的每一個(gè)元素應(yīng)用一個(gè)轉(zhuǎn)換方法大脉,將他們轉(zhuǎn)換成 Observables。然后讓這些 Observables 按順序的發(fā)出元素水孩,當(dāng)concatMap 操作符將源 Observable 的每一個(gè)元素應(yīng)用一個(gè)轉(zhuǎn)換方法镰矿,將他們轉(zhuǎn)換成 Observables。然后讓這些 Observables 按順序的發(fā)出元素俘种,當(dāng)前一個(gè) Observable 元素發(fā)送完畢后秤标,后一個(gè) Observable 才可以開始發(fā)出元素。等待前一個(gè) Observable 產(chǎn)生完成事件后宙刘,才對后一個(gè) Observable 進(jìn)行訂閱苍姜。

let disposeBag = DisposeBag()

let subject1 = BehaviorSubject(value: "??")
let subject2 = BehaviorSubject(value: "??")

let variable = Variable(subject1)

variable.asObservable()
        .concatMap { $0 }
        .subscribe { print($0) }
        .disposed(by: disposeBag)

subject1.onNext("??")
subject1.onNext("??")

variable.value = subject2
subject2.onNext("I would be ignored")
subject2.onNext("??")

subject1.onCompleted()

subject2.onNext("??")

輸出結(jié)果:
next(??)
next(??)
next(??)
next(??)
next(??)

  • connect
    通知可被連接的 Observable 可以開始發(fā)出元素了
connect

可被連接的 Observable 和普通的 Observable 十分相似,不過在被訂閱后不會發(fā)出元素悬包,直到 connect 操作符被應(yīng)用為止衙猪。這樣一來你可以等所有觀察者全部訂閱完成后,才發(fā)出元素布近。

  • create
    通過一個(gè)構(gòu)建函數(shù)完整的創(chuàng)建一個(gè) Observable
create

create 操作符將創(chuàng)建一個(gè) Observable垫释,你需要提供一個(gè)構(gòu)建函數(shù),在構(gòu)建函數(shù)里面描述事件(next吊输,error饶号,completed)的產(chǎn)生過程铁追。

通常情況下一個(gè)有限的序列季蚂,只會調(diào)用一次觀察者的 onCompleted 或者 onError 方法。并且在調(diào)用它們后,不會再去調(diào)用觀察者的其他方法扭屁。

創(chuàng)建一個(gè) [0, 1, ... 8, 9] 的序列:

let id = Observable<Int>.create { observer in
    observer.onNext(0)
    observer.onNext(1)
    observer.onNext(2)
    observer.onNext(3)
    observer.onNext(4)
    observer.onNext(5)
    observer.onNext(6)
    observer.onNext(7)
    observer.onNext(8)
    observer.onNext(9)
    observer.onCompleted()
    return Disposables.create()
}

  • debug

打印所有的訂閱算谈,事件以及銷毀信息

let disposeBag = DisposeBag()

let sequence = Observable<String>.create { observer in
    observer.onNext("??")
    observer.onNext("??")
    observer.onCompleted()
    return Disposables.create()
}

sequence
    .debug("Fruit")
    .subscribe()
    .disposed(by: disposeBag)

輸出結(jié)果:
2017-11-06 20:49:43.187: Fruit -> subscribed
2017-11-06 20:49:43.188: Fruit -> Event next(??)
2017-11-06 20:49:43.188: Fruit -> Event next(??)
2017-11-06 20:49:43.188: Fruit -> Event completed
2017-11-06 20:49:43.189: Fruit -> isDisposed
  • delay

將 Observable 的每一個(gè)元素拖延一段時(shí)間后發(fā)出

delay

delay 操作符將修改一個(gè) Observable,它會將 Observable 的所有元素都拖延一段設(shè)定好的時(shí)間料滥, 然后才將它們發(fā)送出來然眼。

  • delaySubscription

進(jìn)行延時(shí)訂閱

delaySubscription

delaySubscription 操作符將在經(jīng)過所設(shè)定的時(shí)間后,才對 Observable 進(jìn)行訂閱操作葵腹。

  • distinctUntilChanged

阻止 Observable 發(fā)出相同的元素

distinctUntilChanged

distinctUntilChanged 操作符將阻止 Observable 發(fā)出相同的元素高每。如果后一個(gè)元素和前一個(gè)元素是相同的,那么這個(gè)元素將不會被發(fā)出來践宴。如果后一個(gè)元素和前一個(gè)元素不相同鲸匿,那么這個(gè)元素才會被發(fā)出來。

let disposeBag = DisposeBag()

Observable.of("??", "??", "??", "??", "??", "??", "??")
    .distinctUntilChanged()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

輸出結(jié)果:
??
??
??
??
??
  • elementAt

只發(fā)出 Observable 中的第 n 個(gè)元素

elementAt

elementAt 操作符將拉取 Observable 序列中指定索引數(shù)的元素阻肩,然后將它作為唯一的元素發(fā)出带欢。

let disposeBag = DisposeBag()

Observable.of("??", "??", "??", "??", "??", "??")
    .elementAt(3)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

輸出結(jié)果:
??
  • empty

創(chuàng)建一個(gè)空 Observable

empty

empty 操作符將創(chuàng)建一個(gè) Observable,這個(gè) Observable 只有一個(gè)完成事件烤惊。

//創(chuàng)建一個(gè)空 Observable:
let id = Observable<Int>.empty()

//它相當(dāng)于:
let id = Observable<Int>.create { observer in
    observer.onCompleted()
    return Disposables.create()
}
  • error
    創(chuàng)建一個(gè)只有 error 事件的 Observable
error

error 操作符將創(chuàng)建一個(gè) Observable乔煞,這個(gè) Observable 只會產(chǎn)生一個(gè) error 事件。

//創(chuàng)建一個(gè)只有 error 事件的 Observable:
let error: Error = ...
let id = Observable<Int>.error(error)

//它相當(dāng)于:
let error: Error = ...
let id = Observable<Int>.create { observer in
    observer.onError(error)
    return Disposables.create()
}
  • filter

僅僅發(fā)出 Observable 中通過判定的元素

filter

filter 操作符將通過你提供的判定方法過濾一個(gè) Observable柒室。

let disposeBag = DisposeBag()
Observable.of(2, 30, 22, 5, 60, 1)
          .filter { $0 > 10 }
          .subscribe(onNext: { print($0) })
          .disposed(by: disposeBag)

輸出結(jié)果:
30
22
60
  • flatMap

將 Observable 的元素轉(zhuǎn)換成其他的 Observable渡贾,然后將這些 Observables 合并

flatMap

flatMap 操作符將源 Observable 的每一個(gè)元素應(yīng)用一個(gè)轉(zhuǎn)換方法,將他們轉(zhuǎn)換成 Observables雄右。 然后將這些 Observables 的元素合并之后再發(fā)送出來剥啤。

這個(gè)操作符是非常有用的,例如不脯,當(dāng) Observable 的元素本生擁有其他的 Observable 時(shí)府怯,你可以將所有子 Observables 的元素發(fā)送出來。

let disposeBag = DisposeBag()
let first = BehaviorSubject(value: "????")
let second = BehaviorSubject(value: "???")
let variable = Variable(first)

variable.asObservable()
        .flatMap { $0 }
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)

first.onNext("??")
variable.value = second

second.onNext("???")
first.onNext("??")
輸出結(jié)果:

????
??
???
???
??
  • flatMapLatest

將 Observable 的元素轉(zhuǎn)換成其他的 Observable防楷,然后取這些 Observables 中最新的一個(gè)

flatMapLatest

flatMapLatest 操作符將源 Observable 的每一個(gè)元素應(yīng)用一個(gè)轉(zhuǎn)換方法牺丙,將他們轉(zhuǎn)換成 Observables。一旦轉(zhuǎn)換出一個(gè)新的 Observable复局,就只發(fā)出它的元素冲簿,舊的 Observables 的元素將被忽略掉。


tips:與 flatMap 比較更容易理解

let disposeBag = DisposeBag()
let first = BehaviorSubject(value: "????")
let second = BehaviorSubject(value: "???")
let variable = Variable(first)

variable.asObservable()
        .flatMapLatest { $0 }
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)

first.onNext("??")
variable.value = second
second.onNext("???")
first.onNext("??")

輸出結(jié)果:
????
??
???
???
  • from

將其他類型或者數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為 Observable

from

當(dāng)你在使用 Observable 時(shí)亿昏,如果能夠直接將其他類型轉(zhuǎn)換為 Observable峦剔,這將是非常省事的。from 操作符就提供了這種功能角钩。

//將一個(gè)數(shù)組轉(zhuǎn)換為 Observable:
let numbers = Observable.from([0, 1, 2])

//它相當(dāng)于:
let numbers = Observable<Int>.create { observer in
    observer.onNext(0)
    observer.onNext(1)
    observer.onNext(2)
    observer.onCompleted()
    return Disposables.create()
}

//將一個(gè)可選值轉(zhuǎn)換為 Observable
let optional: Int? = 1
let value = Observable.from(optional: optional)

//它相當(dāng)于:
let optional: Int? = 1
let value = Observable<Int>.create { observer in
    if let element = optional {
        observer.onNext(element)
    }
    observer.onCompleted()
    return Disposables.create()
}
  • just

創(chuàng)建 Observable 發(fā)出唯一的一個(gè)元素

just

just 操作符將某一個(gè)元素轉(zhuǎn)換為 Observable吝沫。

//一個(gè)序列只有唯一的元素 0:
let id = Observable.just(0)

//它相當(dāng)于:
let id = Observable<Int>.create { observer in
    observer.onNext(0)
    observer.onCompleted()
    return Disposables.create()
}
  • map

通過一個(gè)轉(zhuǎn)換函數(shù)呻澜,將 Observable 的每個(gè)元素轉(zhuǎn)換一遍

map

map 操作符將源 Observable 的每個(gè)元素應(yīng)用你提供的轉(zhuǎn)換方法,然后返回含有轉(zhuǎn)換結(jié)果的 Observable惨险。

let disposeBag = DisposeBag()
Observable.of(1, 2, 3)
    .map { $0 * 10 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

輸出結(jié)果:
10
20
30
  • merge

將多個(gè) Observables 合并成一個(gè)

merge

通過使用 merge 操作符你可以將多個(gè) Observables 合并成一個(gè)羹幸,當(dāng)某一個(gè) Observable 發(fā)出一個(gè)元素時(shí),他就將這個(gè)元素發(fā)出辫愉。

如果栅受,某一個(gè) Observable 發(fā)出一個(gè) onError 事件,那么被合并的 Observable 也會將它發(fā)出恭朗,并且立即終止序列屏镊。

let disposeBag = DisposeBag()
let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()

Observable.of(subject1, subject2)
    .merge()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

subject1.onNext("A")

subject1.onNext("B")

subject2.onNext("1")

subject2.onNext("2")

subject1.onNext("AB")

subject2.onNext("3")

輸出結(jié)果:
A
B
1
2
AB
3
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市痰腮,隨后出現(xiàn)的幾起案子闸衫,更是在濱河造成了極大的恐慌,老刑警劉巖诽嘉,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔚出,死亡現(xiàn)場離奇詭異,居然都是意外死亡虫腋,警方通過查閱死者的電腦和手機(jī)骄酗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悦冀,“玉大人趋翻,你說我怎么就攤上這事『畜。” “怎么了踏烙?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長历等。 經(jīng)常有香客問我讨惩,道長,這世上最難降的妖魔是什么寒屯? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任荐捻,我火速辦了婚禮,結(jié)果婚禮上寡夹,老公的妹妹穿的比我還像新娘处面。我一直安慰自己,他們只是感情好菩掏,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布魂角。 她就那樣靜靜地躺著,像睡著了一般智绸。 火紅的嫁衣襯著肌膚如雪野揪。 梳的紋絲不亂的頭發(fā)上访忿,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音囱挑,去河邊找鬼。 笑死沼溜,一個(gè)胖子當(dāng)著我的面吹牛平挑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播系草,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼通熄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了找都?” 一聲冷哼從身側(cè)響起唇辨,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎能耻,沒想到半個(gè)月后赏枚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晓猛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年饿幅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戒职。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡栗恩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出洪燥,到底是詐尸還是另有隱情磕秤,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布捧韵,位于F島的核電站市咆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏再来。R本人自食惡果不足惜床绪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望其弊。 院中可真熱鬧癞己,春花似錦、人聲如沸梭伐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽糊识。三九已至绩社,卻和暖如春摔蓝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背愉耙。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工贮尉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人朴沿。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓猜谚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赌渣。 傳聞我的和親對象是個(gè)殘疾皇子魏铅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Introduction: Creating and Subscribing to Observables: Th...
    loongod閱讀 732評論 0 0
  • 一、RxJava操作符概述 RxJava中的操作符就是為了提供函數(shù)式的特性坚芜,函數(shù)式最大的好處就是使得數(shù)據(jù)處理簡潔易...
    BrotherChen閱讀 1,595評論 0 10
  • 一览芳、RxJava操作符概述 RxJava中的操作符就是為了提供函數(shù)式的特性,函數(shù)式最大的好處就是使得數(shù)據(jù)處理簡潔易...
    測天測地測空氣閱讀 626評論 0 1
  • 一鸿竖、RxJava操作符概述 RxJava中的操作符就是為了提供函數(shù)式的特性沧竟,函數(shù)式最大的好處就是使得數(shù)據(jù)處理簡潔易...
    無求_95dd閱讀 2,981評論 0 21
  • Subject創(chuàng)建訂閱Source PublishSubject 最普通的subject,對它訂閱的訂閱者只會收到...
    給傷的你我依然喜歡閱讀 1,098評論 0 1