タコさんブログ

プログラミングメモと小言

RxSwift 入門 その2

RxSwift 入門 その1 - タコさんブログ の続き。
今回は、RxSwiftプレイグラウンドのSubjectsの項。
SubjectObserver 、また Observable の両方として機能し、Hotなオブザーバブルである。 Erik MeijerはSubjectを好んでいないようだ(Why Does E.Meijer not like Subjects?)。

Subjects

RxSwiftでは以下のSubjectクラスが利用できる。

  • PublishSubject
  • ReplaySubject
  • BehaviorSubject
  • Variable

PublishSubject

PublishSubjectはサブスクリプションの後のソースObservable(s)が送信したアイテム(イベント)のみを送信する。

// メモリ管理
let disposeBag = DisposeBag()
// PublishSubject生成
let subject = PublishSubject<String>()
// サブスクライブ
subject.subscribe({ event in
  print("Subscription: 1, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("a"))
subject.on(.Next("b"))
// "b"を送信した後に、
// 更にサブスクライブ
subject.subscribe({ event in
  print("Subscription: 2, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("c"))
subject.on(.Next("d"))

この出力は、

Subscription: 1, event:Next(a)
Subscription: 1, event:Next(b)
Subscription: 1, event:Next(c)
Subscription: 2, event:Next(c)
Subscription: 1, event:Next(d)
Subscription: 2, event:Next(d)

ReplaySubject

ReplaySubjectはオブザーバがいつサブスクライブするかに関わらずソースObservable(s)が送信した全てのアイテム(イベント)をオブザーバに送信する。

// メモリ管理
let disposeBag = DisposeBag()
// ReplaySubject生成
// bufferSizeに指定した数だけアイテムがキャッシュされる
// 全てのアイテムをキャッシュする場合は、createUnbounded を使用する
let subject = ReplaySubject<String>.create(bufferSize: 1)
// サブスクライブ
subject.subscribe({ event in
  print("Subscription: 1, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("a"))
subject.on(.Next("b"))
// "b"を送信した後に、
// 更にサブスクライブ
subject.subscribe({ event in
  print("Subscription: 2, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("c"))
subject.on(.Next("d"))

この出力は、

Subscription: 1,  event:Next(a)
Subscription: 1,  event:Next(b)
Subscription: 2,  event:Next(b)
Subscription: 1,  event:Next(c)
Subscription: 2,  event:Next(c)
Subscription: 1,  event:Next(d)
Subscription: 2,  event:Next(d)

BehaviorSubject

オブザーバがBehaviorSubjectをサブスクライブした時、ソースObservableから送信された最新のアイテム(イベント)を送信し、それから、ソースObservableから送信された他のアイテムを送信し続ける。

// メモリ管理
let disposeBag = DisposeBag()
// BehaviorSubject生成
// 初期値 "Initial value" を与える
let subject = BehaviorSubject<String>(value: "Initial  value")
// サブスクライブ
subject.subscribe({ event in
  print("Subscription: 1, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("a"))
subject.on(.Next("b"))
// "b"を送信した後に、
// 更にサブスクライブ
subject.subscribe({ event in
  print("Subscription: 2, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("c"))
subject.on(.Next("d"))
subject.on(.Completed)

この出力は、

Subscription: 1, event:Next(Initial value)
Subscription: 1, event:Next(a)
Subscription: 1, event:Next(b)
Subscription: 2, event:Next(b)
Subscription: 1, event:Next(c)
Subscription: 2, event:Next(c)
Subscription: 1, event:Next(d)
Subscription: 2, event:Next(d)
Subscription: 1, event:Completed
Subscription: 2, event:Completed

Variable

VariableはBehaviorSubjectをラップする。BehaviorSubjectに対しての利点は、Variableは明示的に complete または error になることがない。また、Variableはdeallocated時に自動的に完了する。

// Variable のdeallocate時にCompleteを見るためにdoで囲む
do {
  // メモリ管理
  let disposeBag = DisposeBag()
  // Variable生成
  // 初期値 "Initial value" を与える
  let variable = Variable("Initial value")
  variable.asObservable().subscribe({ event in
    print("Subscription: 1, event:\(event)")
  }).addDisposableTo(disposeBag)
  variable.value = "a"
  variable.value = "b"
  // valueを"b"に変更した後に、
  // 更にサブスクライブ
  variable.asObservable().subscribe({ event in
    print("Subscription: 2, event:\(event)")
  }).addDisposableTo(disposeBag)
  variable.value = "c"
  variable.value = "d"
  print("--- スコープを抜けて variable はdeallocateされる ---")
}

この出力は、

Subscription: 1, event:Next(Initial value)
Subscription: 1, event:Next(a)
Subscription: 1, event:Next(b)
Subscription: 2, event:Next(b)
Subscription: 1, event:Next(c)
Subscription: 2, event:Next(c)
Subscription: 1, event:Next(d)
Subscription: 2, event:Next(d)
--- スコープを抜けて variable はdeallocateされる ---
Subscription: 1,  event:Completed
Subscription: 2,  event:Completed

参考URL