iOS開發(fā)進階 - RxSwift之Subjects

來自網(wǎng)絡

更新:2019.3.26 RxSwift v4.4 Swift 4.2

前言

上一篇 《iOS開發(fā)進階-RxSwift之Observables》學習了Observables概念,如何創(chuàng)建,訂閱以及清除等。Observables在運行時將值添加到序列中,在將來的某個時機發(fā)射給訂閱者。SubjectsObservables不同在于它同時擔任序列和訂閱者兩個角色。

Subjects

本節(jié)將會學習不同類型的Subject,各個類型Subject是如何工作的及它們之間的不同點。

RxSwift中,提供了四種不同類型的Subject 和兩種解包類型。分別如下:

  • ①、PublishSubject:初始化為空,只發(fā)射最新的元素給訂閱者。
  • ②、BehaviorSubject:有初始值,并且重復發(fā)射最晚一個元素給訂閱者。
  • ③、ReplaySubject:存在一個緩存區(qū),重復發(fā)射符合緩存?zhèn)€數(shù)的元素給新的訂閱者。
  • ④、Variable:是BehaviorSubject的包裝。
  • ⑤、AsyncSubject: 只有在接收到 .completed事件時,發(fā)射最后一個 .next 事件。這個類型 Subject 很少使用。
  • ⑥、PublishRelayBehaviorRelay:包裝相關的 Subject,只接受 .next 事件。

PublishSubjects

當只想將新的事件發(fā)布給訂閱者時,PublishSubject就派上用場了。它也是通過.completed.error事件終止。

下面看一張圓珠圖。最上方的線代表PublishSubject。第二條和第三條代表訂閱者。向上的虛線箭頭代表訂閱,向下的虛線箭頭代表發(fā)射事件。

PublishSubject
  1. 第一個訂閱者在1)之后,只能接收到2)3)。
  2. 第二個訂閱者在2)之后,只能接收到3)。

創(chuàng)建PublishSubject

創(chuàng)建PublishSubject,示例代碼如下:

example(of: "PublishSubject") {
    // 1
    let subject = PublishSubject<String>()
    // 2 
    subject.onNext("1")
    // 3 
    let subscriptionOne = subject.subscribe(onNext: { string in
        print(string)
    })
    // 4
    subscriptionOne.dispose()
}
  1. 創(chuàng)建一個包含字符串類型的PublishSubject
  2. subject中添加一個字符串,此時并沒有任何值輸出因為沒有訂閱。
  3. subject創(chuàng)建一個訂閱,用于打印.next事件。但是此時并沒有打印。
  4. 用于銷毀回收。

由于PublishSubject只能發(fā)射在訂閱之后添加的事件,所以上面并沒有輸出結果。在4)之前添加下面這行代碼:

// subject.on(.next("2"))
// 或者
subject.onNext("2")

subject之前添加了訂閱,所以輸出:

--- Example of: PublishSubject ---
1) 2

接下來在添加一個訂閱,添加如下代碼:

let subscriptionTwo = subject.subscribe { event in             
  print("2)", event.element ?? event)
} 

這種方式訂閱逃逸閉包參數(shù)是Event<String>類型,事件中element屬性是可選值,所以通過??操作取值,如果非nil打印值,否則打印事件本身。

接下來向subject中添加新的元素

subject.onNext("3")

輸出結果:

--- Example of: PublishSubject ---
1) 2
1) 3
2) 3

通過上面的驗證,結果與圓珠圖中相同。

PublishSubject當接收到.completed.error事件也會終止。例如:

subject.onCompleted() 
subject.onNext("4")

此時控制臺只打印出completed并沒有輸出4,因為subject已經(jīng)結束。事實上,任何類型的subject一旦終止,將不會再發(fā)射事件。

BehaviorSubjects

BehaviorSubjects工作方式類似于PublishSubjects,不同點是它會重新發(fā)射最晚的.next事件給新添加的訂閱者。

對應的圓珠圖如下:

BehaviorSubjects
  • 第一條線是當前的subject。
  • 第一個訂閱者,在之后添加由于BehaviorSubjects可以重新發(fā)射最晚的一個元素給新的訂閱者,所以接收到事件序列是① ② ③。
  • 同理第二個訂閱,可以接收的事件序列② ③
// 錯誤枚舉
enum LEError: Error {
    case anError
}

// 錯誤枚舉
enum LEError: Error {
    case anError
}
// 封裝打印方法
func print<T: CustomStringConvertible>(label: String, event: Event<T>) {
    print(label, (event.element ?? event.error) ?? event)
}

example(of: "BehaviorSubject") {
    // 1. 創(chuàng)建 subject
    let subject = BehaviorSubject(value: "Initial Value")
    let disposeBag = DisposeBag()
    subject.subscribe {
        print(label: "1)", event: $0)
    }
    .disposed(by: disposeBag) // 此時控制臺輸出 Initial Value
    
    // 2. 發(fā)送一個事件
    subject.onNext("X") // 輸出 X
    
    // 3. 發(fā)送 Error事件
    subject.onError(LEError.anError) // 輸出 anError
    
    // 4. 終止后不會在輸出
    subject.onNext("Y")
    // 此時再添加訂閱 會輸出什么 ?
    subject.subscribe {
        print(label: "2)", event: $0)
    }
    .disposed(by: disposeBag) // 輸出 2) anError
    // 5. 再添加事件,并不會被輸出。==> 終止后不會再發(fā)送事件
    subject.onNext("Z")
}
  1. 創(chuàng)建subjectdisposeBag。
  2. 添加第一個訂閱,由于 BehaviorSubject的特性會攜帶上次的事件,此時會輸出初始1) Initial Value。
  3. 添加新的元素X,訂閱者會將上一次事件和本次事件同時輸出。
  4. 兩種結束subject的方式,當出現(xiàn)錯誤時終止或者發(fā)送complete時終止。發(fā)送onError使用錯誤終止subject,終止后就不會再發(fā)送新的事件。此時添加 Y 事件訂閱并沒有輸出信息。
  5. 添加新的訂閱,也不能接收 Y 事件,此時 Subject 只會告訴訂閱已經(jīng)終止,因 onError 事件終止。

ReplaySubjects

ReplaySubject存在一個臨時的緩存,會重復將緩存中的元素發(fā)射給新的訂閱。對應的圓珠圖如下:

ReplaySubject

第一個訂閱者(中間線),第二個訂閱(下方線)。兩者的輸出結果都是序列① ② ③。

示例代碼如下:

example(of: "ReplaySubject") {
    // 創(chuàng)建Subject,緩存大小為2。每次會將最晚的兩個發(fā)送給新的訂閱
    let subject = ReplaySubject<String>.create(bufferSize: 2)
    let disposeBag = DisposeBag()
    // 訂閱①
    subject.subscribe {
        print(label: "1)", event: $0)
        }
    .disposed(by: disposeBag)
    // 添加元素
    subject.onNext("1")
    subject.onNext("2")
    // 訂閱②
    subject.subscribe {
        print(label: "2)", event: $0)
        }
        .disposed(by: disposeBag)
    // 添加元素
    subject.onNext("3")
}

輸出結果:

--- Example of: ReplaySubject ---
1) 1
1) 2
2) 1
2) 2
1) 3
2) 3

這里不做過多的解釋,如果理解前兩個這個不難理解。

由于ReplaySubjects的緩存是保存在內(nèi)存中的,所以在使用的時候需要注意大小問題,防止浪費過的的內(nèi)存。盡量避免創(chuàng)建過多的緩存或存放大的對象。

PublishRelay & BehaviorRelay

PublishRelay用來包裝 PublishSubjectBehaviorRelay用來包裝 BehaviorSubject ,被包裝的 Subject 不會終止。通過 accept添加值而不是onNext,由于不會終止也就沒有 .error.completed 事件。

下面看關于 PublishRelay 的例子。

example(of: "PublishRelay") {
    // 1. 創(chuàng)建 relay
    let relay = PublishRelay<String>()
    let disposeBag = DisposeBag()
    // 2. 添加一個元素
    relay.accept("hello")
    // 3. 添加訂閱,PublishRelay 是對 PublishSubject 的封裝具有其特性,并不會輸出 hello
    relay.subscribe(onNext: {
        print($0)
    })
    .disposed(by: disposeBag)
    // 4. 添加元素,
    relay.accept("1") // 輸出: 1
    
    // 5. 如果發(fā)送 .completed 或者 .error 將會怎樣?
//    relay.accept(LEError.anError) // 編譯器報錯。
}

PublishReplay 是對 PublishSubject的封裝具有其特性。

example(of: "BehaviorRelay") {
    // 1. 定義
    let relay = BehaviorRelay(value: "initial Value")
    let disposeBag = DisposeBag()
    
    // 2. 添加value
    relay.accept("New Initial Value")
    // 3. 訂閱
    relay.subscribe {
        print(label: "1)", event: $0)
    }
    .disposed(by: disposeBag)
    // 3. 新Value
    relay.accept("1") // 同 BehaviorSubject
    // 4. 新的訂閱
    relay.subscribe {
        print(label: "2)", event: $0)
    }
    .disposed(by: disposeBag)
    // 5. 新Value
    relay.accept("2")
    // 6. 直接通過 replay 獲取值。
    print(relay.value)
}

Variables

在之前有提到,Variable是對BehaviorSubject的包裝,它會存儲當前的值作為狀態(tài)??梢酝ㄟ^value屬性訪問當前的值,也可以通過它修改值。通過value屬性可以代替onNext(_:)。

Variable包含BehaviorSubject的所有功能,存在初始值,可以重復發(fā)射最晚元素給新的訂閱。不過,想訪問Variable中包裝的BehaviorSubject需要使用asObservable()方法。

Variable與其他subject不同點,第一,不能向Variable中添加.error事件但是可以監(jiān)聽。在銷毀時自動發(fā)送completed事件,不需要手動添加。

example(of: "Variable") {
    let variable = Variable("Init Variable")
    let disposeBag = DisposeBag()
    // 1
    variable.value = "New init value" // 添加新值
    // 2
    variable.asObservable()
        .subscribe {
            print(label: "1)", event: $0) // 輸出前一個值
    }
    .disposed(by: disposeBag)
    // 2
    variable.value = "1"
    variable.asObservable()
        .subscribe {
            print(label: "2)", event: $0)
    }
    .disposed(by: disposeBag)
    // 3
    variable.value = "2"
}

輸出結果:

--- Example of: Variable ---
1) New init value
1) 1
2) 1
1) 2
2) 2

小結

本節(jié)學習了Subjects概念及創(chuàng)建方法。理解基礎概念才能為以后的熟練運用打好基礎。

參考

RxSwift_v4.4

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 本文章內(nèi)部分圖片資源來自RayWenderlich.com 本文結合自己的理解來總結介紹一下RxSwift最基本的...
    FKSky閱讀 3,029評論 4 14
  • 最近比較忙,更新得有點慢,望諒解。 什么是Subject? 上一章我介紹了Observable——一個功能就像一條...
    turtleeeee閱讀 1,971評論 2 14
  • Subject 是 Rx 中的基礎性元素之一(Scheduler, Observable, Operator). ...
    貘鳴閱讀 500評論 0 0
  • 最近在學習RxSwift相關的內(nèi)容,在這里記錄一些基本的知識點,以便今后查閱。 Observable 在RxSwi...
    L_Zephyr閱讀 1,887評論 1 4
  • 今天要給大家說一部小眾諷刺動畫,Animals(紐約屁民)。 美國像這樣披著動畫外衣的諷刺喜劇不在少數(shù),比如說Bo...
    MilletCheong閱讀 437評論 0 1

友情鏈接更多精彩內(nèi)容