iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 5
0
Mobile Development

RxSwift / 30天探索之旅系列 第 5

第 5 天 - Subject (上)

  • 分享至 

  • xImage
  •  

嗨,今天介紹一個既是Observable又是Observer的東西,叫做Subject。

什麼是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
  1. 這是建構一個PublishSubject,跟建構一個物件一樣簡單,他有一個泛型參數,代表它能接收跟發送的類型,我們這邊就是帶入<Int>
  2. Subject可以發送元素給他的訂閱者,這時Subject作為一個Observable的角色,所以也可以寫成
subject.asObservable()
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: disposeBag)
  1. Subject 可以接收.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在不同時間點訂閱。

https://ithelp.ithome.com.tw/upload/images/20200919/2011575191kkQyCS9I.png

寫成程式碼大概會像這樣

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
  1. 程式一開始執行,點擊按鈕只會得到A
  2. 過兩秒再點按鈕,會得到AB,因為此時subject被訂閱第一次了
  3. 過兩秒再點按鈕,會得到ABC,因為此時已經訂閱第二次了

補充
在subject在訂閱前,可以看到多一句delay,是因為要表現出延遲的效果,這裡意思就是說『2秒後進行再訂閱』,這邊我把它封裝起來,原本程式碼長這樣

func delay(_ delayTime: Double, _ closure: @escaping () -> Void) {
    DispatchQueue.main.asyncAfter(deadline: .now() + delayTime) {
        closure()
    }
}

UIKit Extension

上面範例看到button.rx.tap,這是RxSwift對UIButton的事件封裝,像這邊就是對TouchUpInside事件的封裝,簡單來說,就是把非Rx的元件,經由封裝變成Rx,進入Rx世界後,我們就可以方便的使用各種transform、filter...等。

基本上大多數UIKit都有這樣的extension,要怎麼查到封裝實際上做了些什麼,其實很簡單,方法有兩個:

  1. 在 Xcode 按著『option』對著 tap 按一下,就能看到解釋 『Reactive wrapper for TouchUpInside control event.』
  2. 在 Xcode 按著『cmd + shift + o』,輸入UIButton+Rx,就可以看到source code,你就會發現不只tap,連title、image、backgroundImage...等都可以使用,有興趣的各位可以玩玩看

今天講了Subject,明天會介紹更多Subject類型,這幾天會圍繞Subject類型跟使用時機,跟過度使用的話會發生什麼可怕的問題,就這樣,掰


上一篇
第 4 天 - Observable (下)
下一篇
第 6 天 - Subject (下)
系列文
RxSwift / 30天探索之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言