上节末尾,我们提到了Subject。既然它可以同时作为Observable和Observer,我们就直奔主题,从一个叫做PublishSubject
的对象开始,感受下Subject的用法。
PublishSubject
顾名思义,PublishSubject
就像个出版社,到处收集内容,此时它是一个Observer,然后发布给它的订阅者,此时,它是一个Observable。
首先,创建一个PublishSubject
很简单,就像创建一个普通的类对象一样:
let subject = PublishSubject()
其中PublishSubject
的泛型参数,表示它可以订阅到的,以及可以发布的事件类型。
其次,当我们把subject
当作Observer的时候,可以使用onNext
方法给它发送事件:
subject.onNext("Episode1 updated")
第三,当我们把subject
当作Observable的时候,订阅它的代码和订阅普通的Observable完全一样:
let sub1 = subject.subscribe(onNext: {
print("Sub1 - what happened: \($0)")
})
但是执行一下就会发现,控制台上不会显示任何订阅消息,也就是说sub1
没有订阅到任何内容。这是因为PublishSubject
执行的是“会员制”,它只会把最新的消息通知给消息发生之前的订阅者。用序列图表示出来,就是这样的:
可以看到,在红灯之前订阅,就可以订阅到红、绿、蓝全部事件,如果在蓝灯之前订阅,就只能订阅到蓝色事件了。于是,为了订阅到subject
的事件,我们得把订阅的代码,放到通知subject
前面:
let sub1 = subject.subscribe(onNext: {
print("Sub1 - what happened: \($0)")
})
subject.onNext("Episode1 updated")
重新执行下,就能看到Sub1 - what happened: Episode1 updated的通知了。然后,再来观察下面代码的执行结果:
sub1.dispose()
let sub2 = subject.subscribe(onNext: {
print("Sub2 - what happened: \($0)")
})
subject.onNext("Episode2 updated")
subject.onNext("Episode3 updated")
sub2.dispose()
- 首先,在执行过
sub1.dispose()
之后,sub1
就不会再接收来自subject
的任何消息了; - 其次,
subject
有了一个新的订阅者sub2
; - 第三,
subject
又捕获到了两条新的消息。按照刚才的说法,sub2
不会接收到订阅之前的消息,因此,我们应该只能在控制台看到Sub2 - what happened: Episode2 updated和Sub2 - what happened: Episode3 updated这两条消息; - 最后,
sub2
取消对subject
的订阅;
重新执行一下,就能在控制台看到结果了。
BehaviorSubject
如果你希望Subject从“会员制”变成“试用制”,就需要使用BehaviorSubject
。它和PublisherSubject
唯一的区别,就是只要有人订阅,它就会向订阅者发送最新的一次事件作为“试用”。
如图所示,BehaviorSubject
带有一个紫灯作为默认消息,当红灯之前订阅时,就会收到紫色及以后的所有消息。而在绿灯之后订阅,就只会收到绿灯及以后的所有消息了。因此,当初始化一个BehaviorSubject
对象的时候,要给它指定一个默认的推送消息:
let subject = BehaviorSubject(
value: "RxSwift step by step")
然后,当我们再执行先订阅,后发送消息的逻辑时:
let sub1 = subject.subscribe(onNext: {
print("Sub1 - what happened: \($0)")
})
subject.onNext("Episode1 updated")
由于BehaviorSubject
有了一个默认的事件,sub1
订阅之后,就会陆续收到RxSwift step by step和Sub1 - what happened: Episode1 updated的消息了。此时,如果我们再添加一个新的订阅者:
let sub2 = subject.subscribe(onNext: {
print("Sub2 - what happened: \($0)")
})
此时,sub2
就只能订阅到Sub2 - what happened: Episode1 updated消息了。如果我们要让sub2
在订阅的时候获取到过去所有的消息,就需要使用ReplaySubject
。
ReplaySubject
ReplaySubject
的行为和BehaviorSubject
类似,都会给订阅者发送历史消息。不同地方有两点:
-
ReplaySubject
没有默认消息,订阅空的ReplaySubject
不会收到任何消息; -
ReplaySubject
自带一个缓冲区,当有订阅者订阅的时候,它会向订阅者发送缓冲区内的所有消息;
ReplaySubject
缓冲区的大小,是在创建的时候确定的:
let subject = ReplaySubject.create(bufferSize: 2)
这样,我们就创建了一个可以缓存两个消息的ReplaySubject
。作为Observable,它此时是一个空的事件序列,订阅它,不会收到任何消息:
let sub1 = subject.subscribe(onNext: {
print("Sub1 - what happened: \($0)")
})
然后,我们让subject
接收3个事件,sub1
就会收到三次事件订阅:
subject.onNext("Episode1 updated")
subject.onNext("Episode2 updated")
subject.onNext("Episode3 updated")
// Sub1 - what happened: Episode1 updated
// Sub1 - what happened: Episode2 updated
// Sub1 - what happened: Episode3 updated
这时,我们再给subject
添加一个订阅者:
let sub2 = subject.subscribe(onNext: {
print("Sub2 - what happened: \($0)")
})
// Sub2 - what happened: Episode2 updated
// Sub2 - what happened: Episode3 updated
由于subject
缓冲区的大小是2,它会自动给sub2
发送最新的两次历史事件。在控制台中执行一下,就可以看到注释中的结果了。
Variable
除了事件序列之外,在平时的编程中我们还经常需遇到一类场景,就是需要某个值是有“响应式”特性的,例如可以通过设置这个值来动态控制按钮是否禁用,是否显示某些内容等。为了方便这个操作,RxSwift还提供了一个特殊的subject,叫做Variable
。
我们可以像定义一个普通变量一样定义一个Variable
:
let stringVariable = Variable("Episode1")
当我们要订阅一个Variable
对象的时候,要先明确使用asObservable()
方法。而不像其他subject一样直接订阅:
let stringVariable = Variable("Episode1")
let sub1 = stringVariable
.asObservable()
.subscribe {
print("sub1: \($0)")
}
// sub1: next(Episode1)
而当我们要给一个Variable
设置新值的时候,要明确访问它的value
属性,而不是使用onNext
方法:
stringVariable.value = "Episode2"
// sub1: next(Episode2)
最后要说明的一点是,Variable
只用来表达一个“响应式”值的语义,因此,它有以下两点性质:
- 绝不会发生
.error
事件; - 无需手动给它发送
.complete
事件表示完成;
因此,下面的代码都会导致编译错误:
// !!! The following code CANNOT compile !!!
stringVariable.asObservable().onError(MyError.myError)
stringVariable.asObservable().onCompleted()
What's next?
以上,就是RxSwift中4种Subject的用法。至此,我们就一切准备就绪了,接下来,我们就在一个真实的App里,逐步了解如何用RxSwift实现一些之前常见的开发任务。