Posted at

RxSwift のスケジューラ

RxSwift は非同期イベントを受け取るための枠組みです(なお、RxSwift 再入門 という記事も書いていますので、よろしければどうぞ)。

非同期イベントと言いましたが、これは文字通り同期的でないイベントですので、現在のスレッドとは別のスレッドで発生するものです。では、RxSwift でスレッドはどのように扱われるのでしょうか?

Rx でスレッドに当たる概念がスケジューラです。

(正確にはスレッドと完全に対応する関係ではないのですが、多くの場合は1つのスケジューラに1つのスレッドを対応させます。この記事ではそのあたりの詳細は省きます)


余談:Rx の三要素

Rx において重要な要素が3つあります。


  • Observable

  • Operator

  • Scheduler

スケジューラは、Rx の三要素のひとつです。


スケジューラを使う


Observable と Observer

まず、イベント発生元(Observable)とイベント処理(Observer)とを分けて考えます。

Observable.png

hogeObservable // ここはイベント発生元(Observable)

.do(onNext: { _ in
// ここもイベント発生元(Observable)
})
.subscribe(onNext: { _ in
// ここはイベント処理(Observer)
})

このそれぞれについて、スケジューラを指定することができます。

Scheduler.png


observeOn

observeOn を使うと、イベント処理のスケジューラを変えることができます。

observeOn.png

hogeObservable

.observeOn(MainScheduler.instance)
.subscribe(onNext: { _ in
// メインスレッドで動作する
})

例えば、イベント処理で UI 操作をしたい場合にメインスレッドで処理する、という使い方があります。


subscribeOn

subscribeOn を使うと、イベント発生元のスケジューラを変えることができます。

subscribeOn.png

hogeObservable

.subscribeOn(hogeScheduler)
.do(onNext: { _ in
// hogeScheduler で動作する
})
.observeOn(MainScheduler.instance)
.subscribe(onNext: { _ in
// メインスレッドで動作する
})

例えば、イベント発生元は Web API 実行スレッドやデータベースアクセススレッドで処理をする、という使い方があります。


スレッド制御まとめ



  • subscribeOn : イベント発生元(Observable)のスケジューラ指定


  • observeOn : イベント処理(Observer)のスケジューラの指定


注意点

実は subscribeOn で切り替えできない場合があります。それは Observable の性質によるものです。次のセクションで詳しく述べます。


Hot と Cold


subscribeOn が効かない場合

Observable には、Hot なものと Cold なものとの2種類があります。


  • Hot な Observable は subscribeOn で切り替えできない

  • Cold な Observable は subscribeOn で切り替えできる


例:切り替えできないケース

let hogeRelay = BehaviorRelay(value: "A")

let hogeThread = Thread() {
sleep(1)
hogeRelay.accept("B")
sleep(1)
hogeRelay.accept("C")
}
hogeThread.start()

hogeRelay
.subscribeOn(MainScheduler.instance)
.do(onNext: { _ in
// hogeThread で accept したものは hogeThread で動作(subscribeOn が効いていない)
})
.observeOn(MainScheduler.instance)
.subscribe(onNext: { _ in
// メインスレッドで動作する
})

このケースでは、イベント発生元は subscribeOn で指定したスケジューラとは無関係なスレッドで動作します。

ここでは、BehaviorRelay が Hot な Observable であるために、subscribeOn が効いていません。


Observable の分類 : Hot / Cold

Hot と Cold の性質の違いは以下のとおりです。


  • Hot



    • subscribe しなくてもストリームが流れる


    • subscribeOn で制御できない



  • Cold



    • subscribe したらストリームが流れ出す


    • subscribeOn で制御できる



Hot と Cold な Observable は、具体的には以下のものです。


  • Hot


    • Subject や Relay

    • Rx の外側からストリームにイベントを送れる



  • Cold


    • 上記以外はだいたい Cold

    • Rx の制御下にある




Hot / Cold についての私見

私見ですが、Cold な Observable が「普通のもの」、Hot が「特殊なもの」であると考えています。

Cold な Observable は Rx の外側によって操作されることがなく、Rx の制御下にあります。そのおかげで、Rx の仕組みである subscribesubscribeOn で制御が可能です。それができない Hot なものは特殊な存在と感じられます。

ただ一方で、Cold なものだけでは実用上不便そうです。実際に、データバインディング目的で RxSwift を使う場合に、Subject や Relay といった Hot なものは多用します。

そういうわけなので、Cold なものとも Hot なものとも上手につきあうのが良いかと思っています。


補足

この記事は、先日 Mobile Act OSAKA #6 で発表した RxSwiftのスケジューラ をベースに、整理して文章として書き直したものです。