什么是 subject
Subject 同时扮演 observable 和 observer 的角色
RxSwift 提供了四种类型的 subject
Subject | Description |
---|---|
PublishSubject | 没有默认值,只发送订阅之后的事件 |
BehaviorSubject | 有默认值,会发送默认值或最新的一次事件给订阅者 |
ReplaySubject | 没有默认值,自带缓存,有订阅时会将缓存中的事件全部发给订阅者 |
Variable | 将变量做为一个 subject 和 BehaviorSubject 类似 |
Subject 使用
PublishSubjects
首先来看一张图
- 第一条线表示整个事件序列
- 第二条线表示第一个订阅者在事件 A 发生之后订阅了该事件序列,因此第一个订阅者可以接收到 B 和 C
- 第三条线表示第二个订阅者在事件 B 之后订阅了该事件序列,因此它只能接收到 C
代码演示,大概就是
let subject = PublishSubject()
let bag = DisposeBag()
subject.onNext("A")
let subscriptionOne = subject.subscribe({ (event) in
print("1) ", event.element ?? event)
})
subject.on(.next("B"))
let subscriptionTwo = subject.subscribe({ (event) in
print("2) ", event.element ?? event)
})
控制台的打印结果为
1) B
1) C
2) C
此时如果取消第一个订阅,在最后添加如下代码
subscriptionOne.dispose()
subject.on(.next("D"))
此时就只有第二个订阅者可以订阅到事件 D。
当一个 PublishSubject 接收到一个 .completed 或者 .error 事件,它会发送这些终止事件给新的订阅者并且不再发送 .next 事件。然而事实上,它会将终止事件传递给未来的订阅者。
subject.onCompleted()
subject.onNext("E")
subscriptionTwo.dispose()
let disposeBag = DisposeBag()
subject.subscribe({
print("3) ", $0.element ?? $0)
})
.disposed(by: disposeBag)
subject.onNext("?")
打印结果为
2) completed
3) completed
BehaviorSubjects
BehaviorSubject 和 SubjectSubject 工作方式及其类似,区别就在于,BehaviorSubject 会发送最新的 .next 事件给新的订阅者。
由于 BehaviorSubject 总是发出最新的事件,所以必须在创建时给予默认值
enum MyError: Error {
case anError
}
func print(label: String, event: Event) {
print(label, event.element ?? event.error ?? event)
}
- 自定义了一个错误类型
- 创建了一个打印函数,方便打印订阅的事件
let subject = BehaviorSubject(value: "Initial value")
let disposeBag = DisposeBag()
subject.subscribe({
print(label: "1)", event: $0)
})
.disposed(by: disposeBag)
声明了一个带有默认值的 BehaviorSubject 实例,之后订阅它,打印结果为
1) Initial value
由于没有新的事件,因此会将创建实例时的默认值作为最新的事件传递给订阅者。此时,如果我们在订阅之前,通过 onNext 发送一个事件,那么订阅者将会订阅到该事件,而不会接收到默认事件。
在第一次订阅之后,添加如下代码,结果会是什么呢?
subject.onError(MyError.anError)
subject.subscribe({
print(label: "2)", event: $0)
})
.error
事件被订阅到两次,每个订阅者都收到了这个 .error
事件。
BehaviorSubject 在我们想要展示一个包含最近数据的 view 时将非常有用,但,如果你想展示的不止是最近的数据,你想展示更多以往数据,那么,ReplaySubject 将会是你想要的。
ReplaySubject
ReplaySubject 会暂存最新发送的事件到 cache 或者 buffer 中,之后会将这些暂存的事件发送个新的订阅者。下图描述了一个 buffer size 为 2 的 ReplaySubject。第一个订阅者(中间)已经订阅了 ReplaySubject (顶部)因此它接收到了所有事件。第二个订阅者(底部)在 B 事件之后订阅,由于存储了之前的两个事件,因此第二个订阅者也能接收到所有事件。
ReplaySubject 的使用需要谨慎考虑内存问题
用法实例
let subject = ReplaySubject.create(bufferSize: 2)
let disposeBag = DisposeBag()
subject.onNext("1")
subject.onNext("2")
subject.onNext("3")
subject
.subscribe({
print(label: "1) ", event: $0)
})
.disposed(by: disposeBag)
subject
.subscribe({
print(label: "2) ", event: $0)
})
.disposed(by: disposeBag)
首先,定义了一个 Buffer size 为 2 的 ReplaySubject,发送三个 .next
事件,之后,用两个订阅者订阅这个事件。订阅者 1)
和订阅者 2)
都接收到了缓存起来的事件 2
和事件 3
1) 2
1) 3
2) 2
2) 3
接下来添加如下代码
subject.onNext("4")
subject
.subscribe({
print(label: "3) ", event: $0)
})
.disposed(by: disposeBag)
发送新的事件 4
并新增一个订阅。这时订阅者 1)
和订阅者 2)
也能够接收到事件 4
,而新增的订阅者 3)
就只能接收到事件 3
和 4
了。
如果在事件 4
之后,订阅 3)
建立之前,我们添加如下 .error
事件
subject.onError(MyError.anError)
这时,订阅者 1)
2)
也都将收到这个这个 .error
事件。订阅者 3)
将接收到事件 3
4
以及 .error
事件。在接收到终止事件之前,ReplaySubject 都会一直将 buffer 的内容发送给订阅者。
Variables
Variable 和 BehaviorSubject 类似,Variable 的创建包含一个初始值,并且返回最新的事件或者初始值给新的订阅者。为了订阅一个 Variable 需要调用 asObservable()
。
var variable = Variable("Initial value")
let disposeBag = DisposeBag()
variable.value = "New initial value"
variable
.asObservable()
.subscribe({
print(label: "1)", event: $0)
})
.addDisposableTo(disposeBag)
variable.value = "1"
variable
.asObservable()
.subscribe({
print(label: "2)", event: $0)
})
.addDisposableTo(disposeBag)
variable.value = "2"
结果为
1) New initial value
1) 1
2) 1
1) 2
2) 2