RxSwift Subject類型

在 RxSwift 中,Subject 是一種特殊類型,它 同時扮演 Observable(可被訂閱)和 Observer(可接收事件) 的角色,常用于橋接非響應(yīng)式代碼、狀態(tài)管理或事件廣播。

RxSwift 提供了 4 種內(nèi)置 Subject,RxCocoa 補(bǔ)充了 2 種更安全的 Relay(可視為“Subject 的安全封裝”)。

下面將 系統(tǒng)整理所有 6 種類型,包括:

  • 定義
  • 行為特點(diǎn)
  • 使用場景
  • 完整代碼示例
  • 對比總結(jié)表

? 一、四大 Subject(來自 RxSwift)

1. PublishSubject<Element>

?? 行為:

  • 只向訂閱之后的觀察者廣播事件
  • 不緩存任何歷史值
  • 可手動調(diào)用 .onError().onCompleted() 終止流

?? 適用場景:

  • 純事件廣播(如按鈕點(diǎn)擊、通知)
  • 不關(guān)心歷史,只處理未來事件

?? 示例:

let subject = PublishSubject<String>()

subject.onNext("A") // 無訂閱者,丟棄

subject.subscribe { print("Sub1:", $0) }
subject.onNext("B") // Sub1 收到 B

subject.subscribe { print("Sub2:", $0) }
subject.onNext("C") // Sub1 和 Sub2 都收到 C

輸出:

Sub1: next(B)
Sub1: next(C)
Sub2: next(C)

2. BehaviorSubject<Element>

?? 行為:

  • 必須提供初始值
  • 緩存最近一個值
  • 新訂閱者立即收到當(dāng)前值
  • 可被 .onError() / .onCompleted() 終止

?? 適用場景:

  • 表示當(dāng)前狀態(tài)(如登錄狀態(tài)、開關(guān))
  • 需要“總是有值”的語義

?? 示例:

let subject = BehaviorSubject(value: "INIT")

subject.subscribe { print("Sub1:", $0) } // 立即收到 INIT
subject.onNext("X")

subject.subscribe { print("Sub2:", $0) } // 立即收到 X

輸出:

Sub1: next(INIT)
Sub1: next(X)
Sub2: next(X)

?? 風(fēng)險:若調(diào)用 onCompleted(),后續(xù)訂閱者收不到任何值!


3. ReplaySubject<Element>

?? 行為:

  • 緩存最近 N 個事件(FIFO)
  • 新訂閱者收到全部緩存事件
  • 可配置 bufferSize
  • 可被終止

?? 適用場景:

  • 聊天消息(顯示最近幾條)
  • 日志回放
  • 需要“重放歷史”的場景

?? 示例:

let subject = ReplaySubject<String>.create(bufferSize: 2)

subject.onNext("1")
subject.onNext("2")
subject.onNext("3") // 緩存 ["2", "3"]

subject.subscribe { print("Sub1:", $0) } // 收到 2, 3
subject.onNext("4") // 緩存 ["3", "4"]
subject.subscribe { print("Sub2:", $0) } // 收到 3, 4

輸出:

Sub1: next(2)
Sub1: next(3)
Sub1: next(4)
Sub2: next(3)
Sub2: next(4)

4. AsyncSubject<Element>

?? 行為:

  • 只記住 .completed 前的最后一個 .next
  • 只有在調(diào)用 .onCompleted() 后才發(fā)送值
  • 如果出錯(.onError),只傳遞錯誤

?? 適用場景:

  • 異步任務(wù)的最終結(jié)果(如文件下載路徑、初始化完成)
  • 只關(guān)心“最后一刻”的值

?? 示例:

let subject = AsyncSubject<String>()

subject.subscribe { print("Sub1:", $0) }
subject.onNext("A")
subject.onNext("B")
subject.onNext("C")
subject.onCompleted() // 觸發(fā)發(fā)送

輸出:

Sub1: next(C)
Sub1: completed

? 若 never completed,訂閱者永遠(yuǎn)收不到值!


? 二、兩大 Relay(來自 RxCocoa,更安全)

Relay 是 永不失敗、不能完成 的 Subject 封裝,適合 UI 和狀態(tài)管理。


5. BehaviorRelay<Element>

?? 行為:

  • 必須提供初始值
  • 新訂閱者立即收到當(dāng)前值
  • 只能通過 .accept(newValue) 更新
  • 永不發(fā)出 .error.completed
  • 內(nèi)部基于 PublishSubject 封裝

?? 適用場景:

  • UI 狀態(tài)(用戶名、加載中、開關(guān))
  • 替代 BehaviorSubject(更安全)

?? 示例:

let relay = BehaviorRelay(value: false)

relay.subscribe(onNext: { print("isLoggedIn:", $0) }) // 立即打印 false
relay.accept(true) // 更新值

輸出:

isLoggedIn: false
isLoggedIn: true

? 優(yōu)勢:無法被意外完成,適合長期存在的狀態(tài)。


6. PublishRelay<Element>

?? 行為:

  • 不緩存歷史值
  • 新訂閱者只收新事件
  • 只能 .accept(_:) 發(fā)送
  • 永不失敗、不能完成

?? 適用場景:

  • 事件指令(如“顯示彈窗”、“跳轉(zhuǎn)頁面”)
  • 替代 PublishSubject(更安全)

?? 示例:

let relay = PublishRelay<Void>()

relay.subscribe(onNext: { print("Event triggered!") })
relay.accept(()) // 觸發(fā)事件

輸出:

Event triggered!

? 優(yōu)勢:不會因 onCompleted() 導(dǎo)致后續(xù)事件丟失。


? 三、完整對比總結(jié)表

類型 初始值 重放歷史 可 error/complete 永不終止 典型用途
PublishSubject ? ? ? ? 事件廣播(原始)
BehaviorSubject ? 最近 1 個 ? ? 當(dāng)前狀態(tài)(有風(fēng)險)
ReplaySubject ? 最近 N 個 ? ? 歷史回放
AsyncSubject ? 僅最后一個(需 complete) ? ? 異步最終結(jié)果
BehaviorRelay ? 最近 1 個 ? ? UI 狀態(tài)(推薦)
PublishRelay ? ? ? ? 事件指令(推薦)

? 四、如何選擇?—— 決策指南

你的需求 推薦類型
“我想表示一個當(dāng)前狀態(tài),新訂閱者要知道現(xiàn)在是什么” ? BehaviorRelay
“我想廣播一個瞬時事件,如點(diǎn)擊、通知” ? PublishRelay
“我需要讓新訂閱者看到最近 3 條聊天記錄 ReplaySubject(bufferSize: 3)
“我只關(guān)心異步操作的最終結(jié)果 AsyncSubject
“我需要手動控制流的完成或錯誤(極少情況)” BehaviorSubject / PublishSubject

? 五、最佳實(shí)踐建議

  1. 優(yōu)先使用 Relay 而非 Subject

    • BehaviorRelay 代替 BehaviorSubject
    • PublishRelay 代替 PublishSubject
  2. 對外隱藏寫入能力

    private let _isLoading = BehaviorRelay(value: false)
    var isLoading: Observable<Bool> { _isLoading.asObservable() }
    
  3. 避免在 ViewModel 中暴露 Subject
    Relay 更安全,防止外部意外終止流。

  4. 不要濫用大 bufferSize 的 ReplaySubject
    可能導(dǎo)致內(nèi)存問題。


? 六、一句話記憶口訣

  • Publish:后來者,從現(xiàn)在開始聽
  • Behavior:當(dāng)前狀態(tài),隨時可查
  • Replay:回放歷史,N 條為限
  • Async:干完活,只告訴你最后一句
  • Relay:安全版,永不崩潰不終結(jié)

通過合理選擇 Subject/Relay,你可以構(gòu)建出語義清晰、健壯、易維護(hù)的響應(yīng)式系統(tǒng)。在現(xiàn)代 RxSwift 開發(fā)中,BehaviorRelayPublishRelay 應(yīng)作為默認(rèn)選擇,僅在特殊需求下才使用原始 Subject。

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

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

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