LoginSignup
9
3

More than 1 year has passed since last update.

RxSwiftのCold・Hotについて理解を深める

Last updated at Posted at 2021-06-10

はじめに

備忘録としてQiitaに残します。

Cold・Hotを理解する前に

ColdもHotもObservableの性質のことを指します。

・SubjectはHotである

Observableの派生であるSubjectは全てHotなObservableに該当します。

・OperatorはColdである

各種Operatorは内部でObservableを返しますがColdなObservableを返します。

※もちろん、例外もあります。

ColdなObservableの性質

①subscribeされるまで動作しない

ColdなObservableはsubscribeしないと動作しない性質を持っています。

つまり、値を流しません(ストリームが稼働していない状態)。

// sequence(Cold) → map(Cold)
Observable.of("event")
  .map { text -> String in
      print(text)
      return text
  }

// 何も出力されない

subscribeすることによって値が流れます。

Observable.of("event")
  .map { text -> String in
      print(text)
      return text
  }
  .subscribe()
  .disposed(by: disposeBag)

// event

②分岐ができない

これは説明するより、以下のコードで確認した方が分かりやすいです。

// textField(Hot) -> map(Cold)
let observable = textField.rx.text.filterNil().map { text -> String in
     print("\(text.count)")
     return String(describing: text.count)
}

observable.bind(to: label1.rx.text).disposed(by: disposeBag)
observable.bind(to: label2.rx.text).disposed(by: disposeBag)

上記のコードではfilterNil( )でnilを除外し、TextFieldに入力された値をmapで加工して2つのLabelに反映しています。

出力結果はどうなるでしょうか?

0
0
1
1
2
2

文字を入力する度に、map内の処理が2回呼ばれました。

ColdなObservableでは、2つのオブジェクトに対してsubscribeすると
別々の2本のストリームが流れます。

ColdなObservableで複数subscribeした場合、それぞれに対してObservableが複製されてしまうんですね。

1.Hot -> Cold -> Observer

// 複製されてしまう!
2.Hot -> Cold -> Observer

HotなObservableの性質

①subscribeされなくても動作する

HotなObservableではsubscribeしなくても値が流れます。

例えば以下を実行したとします。

var behaviorSubject = BehaviorSubject<String>(value: "value")

// subject(Hot) -> map(Cold)
behaviorSubject.map { text -> String in
    print(text)
    return text
}

// 何も出力されない

ColdなObservableの性質で述べたように何も起きません。
subscribeされるまで動作しないからですね。

ではHotなObservableに変換してみましょう。

冒頭でも述べたように各種OperatorはColdなObservableを返します。

ですがHot変換オペレータと呼ばれるOperatorを使用すればHotなObservableに変換することが出来ます

今回は、その中の1つであるPublish()を使ってみたいと思います。

var behaviorSubject = BehaviorSubject<String>(value: "value")

// subject(Hot) -> map(Cold) -> publish(Hot)
behaviorSubject.map { text -> String in
    print(text)
    return text
}
.publish() // Hot変換する
.connect() // 値が流れる
.disposed(by: disposeBag)

// value

subscribeしていないのに出力されましたね。

これはPublish( )内部でObservableをConnectableObservableに変換して返しているからなんですね(違う記事で詳しくまとめる予定)。

通常のObservableではsubscribeして値を流しますが、ConnectableObservableではconnect()が呼ばれて値を流し始めます

つまり、connect( )を呼びだせばsubscribeしなくても値を流すことができるという訳です。

②分岐できる

では、先ほどのコードをHot変換してみましょう。

// textField(Hot) -> map(Cold) -> publish(Hot)
let observable = textField.rx.text.filterNil().map { text -> String in
     print("\(text.count)")
     return String(describing: text.count)
}
.publish()

observable.bind(to: label1.rx.text).disposed(by: disposeBag)
observable.bind(to: label2.rx.text).disposed(by: disposeBag)

observable.connect().disposed(by: disposeBag)

結果として以下のようになりました。

0
1
2

文字を入力する度に、map内の処理が1回しか呼ばれていない事がわかりますね。

複数subscribeしているのにも関わらず、同じ値がそれぞれに流れているからこそ、このような挙動になります。

1. Hot -> Cold -> Hot  -> Observer
                     | // 分岐するから複製されない!
                       -> Observer

因みに分岐後の値は別の値として扱われるので下記のように個別に処理を加えることが出来ます。

observable.bind(to: label1.rx.text).disposed(by: disposeBag)
observable.map { text -> String in
     "*\(text)*"
}
.bind(to: label2.rx.text)
.disposed(by: disposeBag)

おわりに

RxSwiftは難しいということが理解できました。

正直合っているか分からないので間違っている部分や疑問に思うところがあれば
コメントして下さると有り難いです。

次回はもっとHotなObservableについて理解を深めたいと思います。

参考資料

今日こそ理解するHot / Cold
RxSwift + RxCocoaに関する基礎知識
RxSwiftについてようやく理解できてきたのでまとめることにした(4)
RxJS を学ぼう #4 - COLD と HOT について学ぶ / ConnectableObservable

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3