はじめに
備忘録として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