RxSwift Subject类型

在 RxSwift 中,Subject 是一种特殊类型,它 同时扮演 Observable(可被订阅)和 Observer(可接收事件) 的角色,常用于桥接非响应式代码、状态管理或事件广播。

RxSwift 提供了 4 种内置 Subject,RxCocoa 补充了 2 种更安全的 Relay(可视为“Subject 的安全封装”)

下面将 系统整理所有 6 种类型,包括:

  • 定义
  • 行为特点
  • 使用场景
  • 完整代码示例
  • 对比总结表

✅ 一、四大 Subject(来自 RxSwift)

1. PublishSubject<Element>

🔹 行为:

  • 只向订阅之后的观察者广播事件
  • 不缓存任何历史值
  • 可手动调用 .onError().onCompleted() 终止流

📌 适用场景:

  • 纯事件广播(如按钮点击、通知)
  • 不关心历史,只处理未来事件

💡 示例:

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>

🔹 行为:

  • 必须提供初始值
  • 缓存最近一个值
  • 新订阅者立即收到当前值
  • 可被 .onError() / .onCompleted() 终止

📌 适用场景:

  • 表示当前状态(如登录状态、开关)
  • 需要“总是有值”的语义

💡 示例:

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)

⚠️ 风险:若调用 onCompleted(),后续订阅者收不到任何值!


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
  • 只有在调用 .onCompleted() 后才发送值
  • 如果出错(.onError),只传递错误

📌 适用场景:

  • 异步任务的最终结果(如文件下载路径、初始化完成)
  • 只关心“最后一刻”的值

💡 示例:

let subject = AsyncSubject<String>()

subject.subscribe { print("Sub1:", $0) }
subject.onNext("A")
subject.onNext("B")
subject.onNext("C")
subject.onCompleted() // 触发发送

输出:

Sub1: next(C)
Sub1: completed

❗ 若 never completed,订阅者永远收不到值!


✅ 二、两大 Relay(来自 RxCocoa,更安全)

Relay 是 永不失败、不能完成 的 Subject 封装,适合 UI 和状态管理。


5. BehaviorRelay<Element>

🔹 行为:

  • 必须提供初始值
  • 新订阅者立即收到当前值
  • 只能通过 .accept(newValue) 更新
  • 永不发出 .error.completed
  • 内部基于 PublishSubject 封装

📌 适用场景:

  • UI 状态(用户名、加载中、开关)
  • 替代 BehaviorSubject(更安全)

💡 示例:

let relay = BehaviorRelay(value: false)

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

输出:

isLoggedIn: false
isLoggedIn: true

✅ 优势:无法被意外完成,适合长期存在的状态。


6. PublishRelay<Element>

🔹 行为:

  • 不缓存历史值
  • 新订阅者只收新事件
  • 只能 .accept(_:) 发送
  • 永不失败、不能完成

📌 适用场景:

  • 事件指令(如“显示弹窗”、“跳转页面”)
  • 替代 PublishSubject(更安全)

💡 示例:

let relay = PublishRelay<Void>()

relay.subscribe(onNext: { print("Event triggered!") })
relay.accept(()) // 触发事件

输出:

Event triggered!

✅ 优势:不会因 onCompleted() 导致后续事件丢失。


✅ 三、完整对比总结表

类型 初始值 重放历史 可 error/complete 永不终止 典型用途
PublishSubject 事件广播(原始)
BehaviorSubject 最近 1 个 当前状态(有风险)
ReplaySubject 最近 N 个 历史回放
AsyncSubject 仅最后一个(需 complete) 异步最终结果
BehaviorRelay 最近 1 个 UI 状态(推荐)
PublishRelay 事件指令(推荐)

✅ 四、如何选择?—— 决策指南

你的需求 推荐类型
“我想表示一个当前状态,新订阅者要知道现在是什么” BehaviorRelay
“我想广播一个瞬时事件,如点击、通知” PublishRelay
“我需要让新订阅者看到最近 3 条聊天记录 ReplaySubject(bufferSize: 3)
“我只关心异步操作的最终结果 AsyncSubject
“我需要手动控制流的完成或错误(极少情况)” BehaviorSubject / PublishSubject

✅ 五、最佳实践建议

  1. 优先使用 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
    可能导致内存问题。


✅ 六、一句话记忆口诀

  • Publish:后来者,从现在开始听
  • Behavior:当前状态,随时可查
  • Replay:回放历史,N 条为限
  • Async:干完活,只告诉你最后一句
  • Relay:安全版,永不崩溃不终结

通过合理选择 Subject/Relay,你可以构建出语义清晰、健壮、易维护的响应式系统。在现代 RxSwift 开发中,BehaviorRelayPublishRelay 应作为默认选择,仅在特殊需求下才使用原始 Subject。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容