RxSwift スレッドクイズ (解答・解説編)

## この記事何？

+ RxSwift Advent Calendar 2016の22日目の記事です。
+ クイズを通して、マルチスレッド環境下におけるRxSwiftの挙動と、`observeOn`・`subscribeOn`などのオペレーターや `Driver`・`ControlEvent` などのスレッド周りの性質を解説していきます

+ どのスレッドでなにが起こるのかがわかるようになります！！！
+ あのなんだか難しそうだった`subscribeOn`オペレータもきっと理解できます！！

## クイズについて

長くなってしまうので別記事にしてあります。
純粋にまずクイズにチャレンジしたい人はこちらの記事を先にお読みください！

・[RxSwift スレッドクイズ(問題編)](http://qiita.com/items/917f5f60ab0b5e7ece0d)

いくつかのルールも書いてあります(トップレベルはメインスレッド前提だよ、など)

## はじめに

問題の解説を行う前にいくつか事前知識や用語の説明をしておきます。

詳細は後で説明しますが、どのスレッドでイベントが流れてくるかを判別するには**どのスレッドで`subscribe` が呼ばれたか**が重要になってきます。
ここでいう`subscribe` はもちろん普通に呼ぶ `subscribe` や`bind` もそうなんですが、**内部的なsubscribeも含みます**。

### 内部的なsubscribeとは？


```swift
Observable.of(1, 2, 3)
    .map { $0 * 2 }
    .filter { $0 % 3 == 0 }
```

<img width="320" alt="スクリーンショット 2016-12-14 21.43.10.png (32.1 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/08719d11-faf8-4145-b49e-2f9113a79871.png">


よく見る図としてこういうのがあると思いますが、各Operatorは前のObservable(Operator)を **source** という名前で持っています。例えばこの図で言うと`Filter` のsourceが `Map`、`Map` のsourceが`Sequence` ということになります。

<img width="320" alt="スクリーンショット 2016-12-14 21.43.53.png (54.1 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/02692a51-22f2-4f14-adb3-30803571f6d1.png">


ColdなOperatorは`subscribe`が呼ばれるとsource(つまり前のObservable)のsubscribeを行います。[^1] これを指して**内部的なsubscribe** と(僕は)呼んでいます。

<img width="320" alt="スクリーンショット 2016-12-14 21.47.47.png (73.3 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/89c8890b-88c8-48d5-beae-1a21576f2458.png">



[^1]: 実際には`Sink` と呼ばれるクラスがsourceのobserverになります。`Map` オペレーターには `MapSink` があるように基本的には各オペレータについて`XXXSink` が存在し、sourceのsubscribeを行っています。

これらを含め**どのスレッドで`subscribe` が呼ばれたかによってイベントが流れてくるスレッドが決まってきます。**

### subscribe と on

実際に興味があるのは「どのスレッドでイベントが流れてくるか」であることが多いのですが、これがsubscribeされるスレッドによって決まってくる以上、2つの操作についてどのスレッドで行われるかを考えていく必要があります。

内部実装での名前に合わせて、

+ subscribeする操作をそのまま「**subscribe**」
+ イベントを送出する操作を「**on**」

と呼ぶことにして、それぞれどのスレッドで行われるかを解説してきます。

<img width="320" alt="スクリーンショット 2016-12-14 21.49.52.png (96.3 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/283dbb6f-efa0-457a-af2a-da55dae7608c.png">


## 第1問の答えと解説

(1)のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第1問
Observable.of(1, 2, 3)
    .map { $0 * 2 } // (1)
    .subscribe()
```


**答えはメインスレッドです。** 答えだけ見れば簡単じゃん！と思うかもしれませんが、なにがどう動いてメインスレッドで動作したのかをみてみると、このシンプルな例の中にスレッドに関する重要なルールをいくつかみることができます。


### スレッドに関するルール

1. **通常のOperatorは 同じスレッドで次の `on` / `subscribe` を呼ぶ**
1. **Create系Operatorは `subscribe` が呼ばれたスレッドで`on` を呼ぶ**

<img width="480" alt="スクリーンショット 2016-12-14 21.57.19.png (109.1 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/869feba3-6471-49a4-a0eb-716a9224983a.png">


ルール1.の挙動は以下で確認できます。

+ `Map` は`subscribe`が呼ばれたスレッドで次の `subscribe`を呼んでいる
+ `Map` は`on` が呼ばれたスレッドで次の `on` を呼んでいる

つまり、明示的にスレッドを変えるような操作を何もしていなければ**動作するスレッドは変わらない**ということです。

また、`Sequence` において、ルール2.が確認できます。

+ `Sequence` は`subscribe` が呼ばれたスレッドで `on` を呼んでいる

その他、`just`や`create`など、Create系のOperatorは**subscribeが呼ばれたスレッドでイベントを送出します**。


## 第2問の答えと解説

(1)のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第2問
let o = Observable.of(1, 2, 3)
    .map { $0 * 2 } // (1)

backgroundQueue.async {
    o.subscribe() // バックグラウンドでsubscribe
}
```

答えは **バックグラウンドスレッドです。**
上記のルールにそって考えればバックグラウンドスレッドで呼んだ `subscribe` を起点として、すべての `subscribe` ・`on` がバックグラウンドで行われることがわかると思います。


<img width="480" alt="スクリーンショット 2016-12-14 22.02.31.png (108.2 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/3ca773ed-cc80-4ced-b632-9f7dd6497dad.png">


最初に言った**「どのスレッドでsubscribeが呼ばれるかが重要」**の意味がだんだん実感できてきたのではないでしょうか！


## 第3問の答えと解説

(1)のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第3問
let o = Observable.of(1, 2, 3)
    .map { $0 * 2 } // (1)
    .publish()

o.subscribe()

backgroundQueue.async {
    o.connect()
}
```

Hot変換が関連しているパターンです。
`subscribe`はメインスレッドで行っていますが、 `connect` をバックグラウンドで行っています。全体の流れはどうなるでしょうか？

答えは **バックグラウンドスレッドです**。

<img width="480" alt="スクリーンショット 2016-12-14 22.14.00.png (114.7 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/ba630ee8-6b8a-4a5d-856f-f319426a634e.png">



途中で `ConnectableObservable` が挟まっています。
`ConnectableObservable` は`connect` を呼んだタイミングで内部的にsourceの`subscribe` を行います。その際のスレッドはもちろん `connect` を呼んだスレッドになります。

末端の`subscribe`ではだけでなく、内部の `subscribe` の動きが関係してくるのでちょっと難しかったのではないでしょうか！


## 第4問の答えと解説

(1)のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第4問
let o = Observable.of(1, 2, 3)
    .map { $0 * 2 } // (1)
    .share()

backgroundQueue.async {
    // バックグラウンドスレッド
    o.subscribe()

    mainQueue.async {
        // メインスレッド
        o.subscribe()
    }
}
```

`share` が呼ばれているのと、2つのスレッドで`subscribe` を呼んでいます。

答えは**バックグラウンドスレッド**です。


<img width="480" alt="スクリーンショット 2016-12-14 22.35.06.png (109.0 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/c5a4bc5b-f5ef-43d0-96a3-7408702b1a5c.png">


`share` オペレーターは以下のように定義されています。

```swift
public func share() -> Observable<E> {
    return self.publish().refCount()
}
```

`refCount` オペレータは`ConnectableObservable` に対して`connect`の代わりに呼ぶことのできるメソッドで、**最初に`subscribe`されたときに内部で `connect`を勝手に呼んでくれます。**

```swift
if _parent._count == 0 {
    _parent._count = 1
    _parent._connectableSubscription = _parent._source.connect()
}
```

今回は先にバックグラウンドスレッドで `subscribe` を読んでいるので、結果すべての操作が バックグラウンドで行われています。
またHot変換されているのでメインスレッドの方での `subsribe` によって再度 `map` に値が流れてくることはない点にも注意してください。


いかがでしょうか？？ ここまでが純粋に `subscribe` をいろんなスレッドで呼んだ場合の問題になります。
次は `Scheduler` を織り交ぜた場合の問題になります！

## 第5問の答えと解説

(1)のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第5問
let o = Observable.of(1, 2, 3, scheduler: mainScheduler)
    .map { $0 * 2 } // (1)

backgroundQueue.async {
    o.subscribe()
}
```

`subscribe` こそバックグラウンドで行っていますが、 **`mainScheduler`**が 設定されている点に注意してください。

答えは **メインスレッド** になります。

create系のoperatorにはSchedulerを設定することができ、その際には以下のルールが適用されます。

### スレッドに関するルール

+ **Schedulerが設定されているCreate系Operatorは、`subscribe` されたスレッドと関係なくそのスレッド(Scheduler)で`on` を呼ぶ。**

<img width="400" alt="スクリーンショット 2016-12-14 22.44.08.png (75.6 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/6ff96822-a96f-4f80-9dc9-b2c0dca514b9.png">


バックグラウンドで `subscribe` していますが、MainSchedulerを設定しているため、`on` はメインスレッドで呼ばれているのが確認できるかと思います。


Create系Operatorに設定するSchedulerのルールは以上になりますが、他にもSchedulerを設定する術があります。
それが `observeOn` / `subscribeOn` オペレータです。

## 第6問の答えと解説

(1), (2) のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第6問
Observable.of(1, 2, 3)
    .map { $0 * 2 }  // (1)
    .observeOn(backgroundScheduler)
    .map { $0 * 2 } // (2)
    .subscribe()
```

答えを先に書いてしまいますが、

+ **(1) はメインスレッド**
+ **(2)はバックグラウンドスレッド**

でイベントが流れてきます。

さて、 `observeOn` オペレータがでてきましたがこちらは使い慣れている方も多いのではないでしょうか。
この記事での用語を使うと、このオペレータは**`on` するスレッドを変更するためもの**  です。

<img width="480" alt="スクリーンショット 2016-12-14 22.59.24.png (128.0 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/cbe2f78a-86f9-4636-8106-55e3d4b64b34.png">

`observeOn` の前後で `on` の動作スレッドが変化しているのが確認できるかと思います。

### スレッドに関するルール

+ **`observeOn` は `on` が行われる Scheduler(スレッド) を変更する**


  
ここまでくれば、**なんだか難しそうだった  `subscribeOn` オペレータの挙動も容易に理解できそうな気がしてきますね！！！！**

## 第7問の答えと解説

(1), (2) のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第7問
Observable.of(1, 2, 3)
    .map { $0 * 2 } // (1)
    .subscribeOn(backgroundScheduler)
    .map { $0 * 2 } // (2)
    .subscribe()
```

答えは **両方ともバックグラウンドスレッド** で動作します。

<img width="600" alt="スクリーンショット 2016-12-14 23.12.48.png (126.3 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/27171e95-60e6-474c-848e-19e0d94671a1.png">

もうだいたい想像はついているかもしれませんが、この記事での用語を使うと、`subscribeOn`は**`subscribe` するスレッドを変更するためもの**  です。
上の例でも `subscribeOn` の前後で`subscribe`のスレッドが変わっていることが確認できると思います。

まぁざっくり言ってしまえば以下は同じことです。

```swift
o.subscribeOn(backgroundScheduler).subscribe()
```

```swift
backgroundQueue.async {
    o.subscribe()
}
```


### スレッドに関するルール

+ **`subscribeOn` は `subscribe` が行われる Scheduler(スレッド) を変更する**



これで大体のルールは説明し終わりました！！あとは組み合わせの問題です！！



## 第8問の答えと解説

(1), (2) のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第8問
Observable.of(1, 2, 3, scheduler: backgroundScheduler)
    .map { $0 * 2 } // (1)
    .observeOn(mainScheduler)
    .map { $0 * 2 } // (2)
    .subscribe()
```

答えは

+ (1)はバックグラウンドスレッド
+ (2)はメインスレッド

です。

<img width="600" alt="スクリーンショット 2016-12-14 23.25.18.png (128.6 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/7f3a78e0-6660-400e-8d43-0f34f0f6c821.png">


メインスレッドで`subscribe`を行っていますが、 `backgroundScheduler` が設定されているので `on` がバックグラウンドスレッドで呼ばれ始めます。しかし`observeOn` で `on` を行うスレッドが変更されているので (2)にはメインスレッドでイベントが流れてきます。


## 第9問の答えと解説

(1), (2) のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift::第9問
Observable.of(1, 2, 3, scheduler: mainScheduler)
    .map { $0 * 2 } // (1)
    .subscribeOn(backgroundScheduler)
    .map { $0 * 2 } // (2)
    .subscribe()
```

答えは**両方ともメインスレッドです**。

<img width="600" alt="スクリーンショット 2016-12-14 23.28.39.png (127.5 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/72e47466-d9fb-4cfa-8677-6da4cd89fb3e.png">


もう完璧ですね！！！
最後にRxCocoaのクラスをいくつか紹介して終わりにします。もちろんここまでのルールもでてきますよ！！

## 第10問の答えと解説

(1), (2) のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第10問
let o = Observable.of(1, 2, 3)

ControlEvent<Int>(events: o)
    .map { $0 * 2 } // (1)
    .subscribeOn(backgroundScheduler)
    .map { $0 * 2 } // (2)
    .subscribe()
```

`ControlEvent` や `ControlProperty` はUIKitとの連携に使われてるやつです。
スレッド以外の性質もあるのですが、今回はスレッドについてのみ着目します。

なんとなくメインスレッドになりそうな感じはありますが、実際はどんな挙動になるのでしょうか？

答えは**(1), (2) ともにメインスレッドです。**
`ControlEvent` は  `Observable` を元に作ることができますが、以下のように`subscribeOn` をかませています。

```swift
public init<Ev: ObservableType>(events: Ev) where Ev.E == E {
    _events = events.subscribeOn(ConcurrentMainScheduler.instance)
}
```

つまり**`ControlEvent`の `subscribe` は常にメインスレッドで行われます。**


<img width="600" alt="スクリーンショット 2016-12-14 23.39.45.png (133.7 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/8f88a78d-6653-4c44-a365-64bb604dbe3d.png">

`Map` の前後で `subscribe` のスレッドが変わっていますが、要は `subscribeOn` オペレータが挟まってるだけです。


では次の問題のように`ControlEvent` を作ったらどうなるでしょうか？？

## 第11問の答えと解説

(1), (2) のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第11問
let o = Observable.of(1, 2, 3, scheduler: backgroundScheduler)

ControlEvent<Int>(events: o)
    .map { $0 * 2 } // (1)
    .subscribeOn(backgroundScheduler)
    .map { $0 * 2 } // (2)
    .subscribe()
```


答えは **(1), (2) ともバックグラウンドスレッドです。**

<img width="600" alt="スクリーンショット 2016-12-14 23.41.25.png (131.1 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/14/2884/5056a43c-a49e-4bdf-b06b-93edefda6b76.png">

UIKitの連携に使うのだからメインスレッドになりそうなイメージですが、上記の通り `subscribeOn` をかませているだけなので別に`on` をメインスレッドで流すことを保証はしていません。なのでこの問題のようにバックグラウンドで`on` を流すように作ることも可能です。

まぁこんな使い方はされないとは思うので、RxCocoaで用意されてるものはメインスレッドで流れてくると覚えておいても差し支えないと思います。。


最後はDriverに関する問題です。

## 第12問の答えと解説

(1) のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第12問
let d = Observable.of(1, 2, 3)
    .asDriver(onErrorJustReturn: 0)
    .map { $0 * 2 } // (1)

backgroundQueue.async {
    d.drive()
}
```

`Driver` をバックグラウンドスレッドで`subscribe` ( `drive` ) しようとするとどうなるでしょう？？

答えは....**実行時エラー**になります。
メインスレッドでしか`drive` は呼べないんですね〜〜。 `ControlEvent` のときのように `subscribeOn` オペレータがはさまってるわけでもなく、`rxFatalError` が呼ばれてクラッシュします。

```swift
public func drive<O: ObserverType>(_ observer: O) -> Disposable where O.E == E {
    MainScheduler.ensureExecutingOnScheduler(errorMessage: driverErrorMessage) // ここでチェックされてる
    return self.asSharedSequence().asObservable().subscribe(observer)
}
```

`subscribe` がバックグラウンドで呼べないことはわかりましたが、 `on` はどうなるでしょうか？

## 第13問の答えと解説

(1) のmapにはどのスレッドでイベントが流れてくるでしょう？

```swift:第13問
Observable.of(1, 2, 3, scheduler: backgroundScheduler)
    .asDriver(onErrorJustReturn: 0)
    .map { $0 * 2 } // (1)
    .drive()
```

答えは **メインスレッドです。**


<img width="480" alt="スクリーンショット 2016-12-17 22.30.21.png (104.4 kB)" src="https://img.esa.io/uploads/production/attachments/2245/2016/12/17/2884/6af6a69b-a93b-4c1d-929d-251ef9b5a702.png">

`asDriver` の実装を見てみると`observeOn` オペレータが呼ばれていることがわかります。

```swift
public func asDriver(onErrorJustReturn: E) -> Driver<E> {
    let source = self
        .asObservable()
        .observeOn(DriverSharingStrategy.scheduler)
        .catchErrorJustReturn(onErrorJustReturn)
    return Driver(source)
}
```

なのでDriver以降はメインスレッドで `on` が呼ばれるようになります。


## 最後に


いかがだったでしょうか！ 実は以前社内に布教活動をしたときの資料の焼き直しなのでよければそちらも御覧ください。

+ [マルチスレッドRxSwift @ 社内RxSwift勉強会](https://speakerdeck.com/ukitaka/marutisuretudorxswift-at-she-nei-rxswiftmian-qiang-hui)
+ [RxSwift コードリーディングの勘所@社内RxSwift勉強会](https://speakerdeck.com/ukitaka/rxswift-kodorideingufalsekan-suo-at-she-nei-rxswiftmian-qiang-hui)


ここまで長々と解説しておいてアレですが、動作スレッドを変えたい目的であれば `observeOn` や `Driver` が使えればアプリケーション開発では十分かなぁとは思います。
(いままでフルRxSwiftのプロジェクトに2つほど関わってきましたが、少なくとも僕は `subscribeOn` が必要な場面に出会ったことはありません。そして、動作スレッドを変える目的で `subscribeOn`を使わないほうが良いです。。。)


ViewModelとViewのバインディングだけにRxを使っているとなかなかメインスレッド以外で処理する機会もないかもしれませんが、非同期処理の抽象化として`Observable` を使っているといろんな処理を別スレッドで行いたいこともでてくるかと思うので、その際にはこの記事をお役立てください！

明日はいつも僕の隣で仕事をしている @tarunonくんです！！！乞うご期待！！！