嗨,今天介紹一個既是Observable又是Observer的東西,叫做Subject。
如同前面介紹,他同時是Observable跟Observer,這是什麼意思呢?讓我們直接從程式碼認識它,Subject有很多種,今天我們先以PublishSubject做範例,其餘明天再介紹
// 1
let subject = PublishSubject<Int>()
// 2
subject
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
// 3
subject.onNext(1)
subject.onNext(2)
subject.onNext(3)
執行結果
1
2
3
Completed
<Int>
。subject.asObservable()
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
.next
, .completed
, .error
事件,所以,subject.onNext(1)
表示,『Subject接收到數字1』,這也表示,這時的Subject作為一個Observer的角色,所以也可以寫成subject.asObserver().onNext(1)
順帶一提,PublishSubject最一開始會是空的,你要先訂閱,才能收到往後發出的新元素,就像是你向報商訂雜誌,你會從下一期才能得到雜誌,這也是為什麼上面範例會先訂閱再發送,大家可以嘗試把訂閱的程式碼往後搬到.onNext()
後面,他就印不出數字了。
再複習一次,Subject的特性是『它接收到.next
事件後,可以再發送給它的訂閱者』,這很適合作為一個中間者的角色,可以用於接收資訊,在發送給訂閱者,下面我們來看看一個範例。
建立一個Subject來接收按鈕的點擊事件,接這在把訊息發送給Observer 1跟Observer 2;以圖示表示,就會像下面這張圖這樣,藍色代表訂閱,紅色代表發送事件,另外,為了表示在不同時間點訂閱,會收到不同的訊息,我刻意將observer 1跟2在不同時間點訂閱。
寫成程式碼大概會像這樣
let subject = PublishSubject<Void>()
// 1
button.rx.tap
.subscribe(onNext: { _ in
print("=== A Tap ===")
subject.onNext(())
})
.disposed(by: disposeBag)
// 2
delay(2) {
subject
.subscribe(onNext: {
print("B Tap")
})
.disposed(by: self.disposeBag)
}
// 3
delay(4) {
subject
.subscribe(onNext: {
print("C Tap")
})
.disposed(by: self.disposeBag)
}
執行結果
=== A Tap ===
=== A Tap ===
B Tap
=== A Tap ===
B Tap
C Tap
補充
在subject在訂閱前,可以看到多一句delay
,是因為要表現出延遲的效果,這裡意思就是說『2秒後進行再訂閱』,這邊我把它封裝起來,原本程式碼長這樣
func delay(_ delayTime: Double, _ closure: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + delayTime) {
closure()
}
}
上面範例看到button.rx.tap
,這是RxSwift對UIButton的事件封裝,像這邊就是對TouchUpInside事件的封裝,簡單來說,就是把非Rx的元件,經由封裝變成Rx,進入Rx世界後,我們就可以方便的使用各種transform、filter...等。
基本上大多數UIKit都有這樣的extension,要怎麼查到封裝實際上做了些什麼,其實很簡單,方法有兩個:
UIButton+Rx
,就可以看到source code,你就會發現不只tap,連title、image、backgroundImage...等都可以使用,有興趣的各位可以玩玩看今天講了Subject,明天會介紹更多Subject類型,這幾天會圍繞Subject類型跟使用時機,跟過度使用的話會發生什麼可怕的問題,就這樣,掰