RxSwift介紹(三)——更加靈活的Subject

前一篇文章講述 RxSwift 框架中最重要的類 Observable<T> ,但是其局限性只能作為被訂閱者被動(dòng)接收信號(hào)并響應(yīng)事件。項(xiàng)目中避免不了主動(dòng)發(fā)出信號(hào)操作的情況,這時(shí)就需要 Subject 類來完成。與之前RAC框架中的 Subject 類功能非常相似,既能攻也能受,是不僅可以成為可觀察對(duì)象被動(dòng)接受事件,還可以成為觀察者主動(dòng)發(fā)送事件。

Subject 其訂閱者也是 Observable,首先可以動(dòng)態(tài)地接受新值,其次當(dāng) subject 值更新時(shí),會(huì)通過 event 把新值發(fā)送給所有的訂閱者。

在 RxSwift 框架中,提供了四種類型的 subject,首先要了解的一點(diǎn)就是提供的四種 subject 創(chuàng)建方式最主要的區(qū)別:當(dāng)一個(gè)新的訂閱者訂閱到subject對(duì)象時(shí),能否收到 subject 以前發(fā)出過的舊 event,如果能,接收的數(shù)量又有不同。

  1. PublishSubject 最普通的 subject ,不需要初始值就可以創(chuàng)建,而且從訂閱者開始訂閱的時(shí)間點(diǎn)起,可以收到 subject 發(fā)出的新 event,而不會(huì)收到在訂閱前已發(fā)出的 event
  2. BehaviorSubject 當(dāng)訂閱者訂閱 subject 時(shí),會(huì)立即收到 BehaviorSubject 上一個(gè)發(fā)出的 event,之后與 PublishSubject 功能相同
  3. ReplaySubject 除了包含 PublishSubject 的功能,還可以手動(dòng)設(shè)置訂閱者接收到舊的 event 個(gè)數(shù)。因此,在使用時(shí)必須在創(chuàng)建時(shí)設(shè)置 bufferSize,表示將會(huì)返回給訂閱者對(duì)應(yīng)個(gè)數(shù)最近緩存的舊 event
    (注:若一個(gè)訂閱者去訂閱已經(jīng)結(jié)束的 ReplaySubject ,除了會(huì)收到緩存的 .next 的 event之外,還會(huì)收到終結(jié)該 ReplaySubject 的 .error 或 .completed 的event)
    在實(shí)際開發(fā)過程中,ReplaySubject 緩存機(jī)制使用了數(shù)組結(jié)構(gòu),所以當(dāng)有大量 ReplaySubject 對(duì)象時(shí)可能導(dǎo)致內(nèi)存暴增。另外,如果緩存對(duì)象是圖片、視頻等極耗內(nèi)存的資源時(shí)也可能導(dǎo)致內(nèi)存問題。所以 ReplaySubject 不可濫用且緩存區(qū)大小必須合理進(jìn)行設(shè)置,必要時(shí)可手動(dòng)進(jìn)行釋放管理
  4. Variable 本身是對(duì) BehaviorSubject 封裝,創(chuàng)建時(shí)也必須設(shè)置一個(gè)默認(rèn)值。繼承自 BehaviorSubject ,那么就能向訂閱者發(fā)出上一個(gè) event 與新的 event。與 BehaviorSubject 不同的是,Variable還會(huì)把當(dāng)前發(fā)出的值保存為自己的狀態(tài),同時(shí)在銷毀時(shí)自動(dòng)發(fā)送 .completed event,不需要也不能手動(dòng)給 Variable 發(fā)送終結(jié)事件 .completed 或 .error 來終結(jié)。
    換個(gè)方式理解,Variable 有一個(gè) value 屬性,當(dāng)改變 value 屬性的值時(shí)就相當(dāng)于調(diào)用一般 Subjects 的 onNext() 方法,而這個(gè)最新的 onNext() 的值就被保存在 value 屬性里,直到再次修改 value
    (注:Variable 本身沒有提供 subscribe() 方法,但是所有 Subjects 都有一個(gè) asObservable() 方法。可以使用這個(gè)方法返回這個(gè) Variable 的 Observable 類型,拿到這個(gè) Observable 類型就能訂閱它了)

介紹了以上四種 subject ,接下來貼代碼并附上運(yùn)行打印截圖,有興趣的可以copy下來運(yùn)行一遍,了解其 event 執(zhí)行順序

PublishSubject代碼示例

        let subject = PublishSubject<String>()
        subject.onNext("first signal")
        subject.subscribe(onNext: { (event) in
            print("first event is"+event)
        }, onCompleted: {
            print("completed first")
        }) {
            print("first :銷毀了")
        }.disposed(by: disposeB)
        
        subject.onNext("second signal")
        subject.subscribe(onNext: { (event) in
            print("second event is "+event)
        }, onCompleted: {
            print("completed second")
        }) {
            print("second :銷毀了")
        }.disposed(by: disposeB)
        
        //讓subject結(jié)束,后面再進(jìn)行訂閱
        subject.onCompleted()
        
        subject.onNext("third signal")
        subject.onNext("fourth signal")
        subject.subscribe(onNext: { (event) in
            print("this is another"+event)
        }, onCompleted: {
            print("completed another")
        }) {
            print("another :銷毀了")
        }.disposed(by: disposeB)
PublishSubject打印結(jié)果

BehaviorSubject代碼示例

        let subject = BehaviorSubject(value: "first signal")
        subject.onNext("another first signal") //會(huì)替換了 first signal 的信號(hào)
        subject.subscribe(onNext: { (event) in
            print(event)
        } , onCompleted: {
            print("completed")
        }) {
            print("第一個(gè)銷毀了")
        }.disposed(by: disposeB)
        
        subject.onNext("second signal")

        subject.onNext("third signal") //這里試圖替換上面的 second signal 的event
        subject.subscribe(onNext: { (event) in
            print(event)
        } , onCompleted: {
            print("completed")
        }) {
            print("第二個(gè)銷毀了")
        }.disposed(by: disposeB)
        
        subject.onError(NSError(domain: "myError", code: 10010, userInfo: ["myUserInfo":"10010錯(cuò)誤"]))
BehaviorSubject打印結(jié)果

ReplaySubject代碼示例

        //設(shè)置緩存最近2個(gè)event
        let subject = ReplaySubject<String>.create(bufferSize: 2)
        subject.onNext("first")
        subject.onNext("second")
        subject.onNext("third")

        subject.subscribe(onNext: { (event) in
            print(event)
        }, onError: { (error) in
            print(error)
        }, onCompleted: {
            print("這是一個(gè) 完成")
        }) {
            print("銷毀了")
        }.disposed(by: disposeB)

        subject.onCompleted()//現(xiàn)在終結(jié)subject
        
        subject.subscribe(onNext: { (event) in
            print(event)
        }, onError: { (error) in
            print(error)
        }, onCompleted: {
            print("完成之后的訂閱完成")
        }) {
            print("完成之后銷毀了")
        }.disposed(by: disposeB)
ReplaySubject打印結(jié)果

Variable代碼示例

        let subject = Variable("first")
        subject.value = "second"
        subject.asObservable().subscribe(onNext: { (event) in
            print(event)
        }, onError: { (error) in
            print(error)
        }, onCompleted: {
            print("Variber訂閱完成")
        }) {
            print("Variber銷毀")
        }
        .disposed(by: disposeB)
        
        subject.value = "third"

Variable打印結(jié)果

打印結(jié)果中給出了一個(gè)警告,在其GitHub的issue鏈接中,提到了 Variable 要在接下來的版本里刪除,請(qǐng)用 BehaviorRelay 代替,其實(shí) Variable 封裝,還是比較順手。貌似現(xiàn)在UI層的很多都是 Variable 來管理,但 RxSwift 也封裝了很多關(guān)于UI的,issue的回復(fù)中說以后會(huì)銷毀,注意是銷毀 Variable 這個(gè)類。


該文章首次發(fā)表在 簡(jiǎn)書:我只不過是出來寫寫代碼 博客,并自動(dòng)同步至 騰訊云:我只不過是出來寫寫iOS 博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容