美文网首页iOS日常积累
16.5四种Subject的基本用法

16.5四种Subject的基本用法

作者: CDLOG | 来源:发表于2019-07-17 18:07 被阅读27次

    上节末尾,我们提到了Subject。既然它可以同时作为Observable和Observer,我们就直奔主题,从一个叫做PublishSubject的对象开始,感受下Subject的用法。

    PublishSubject

    顾名思义,PublishSubject就像个出版社,到处收集内容,此时它是一个Observer,然后发布给它的订阅者,此时,它是一个Observable。

    首先,创建一个PublishSubject很简单,就像创建一个普通的类对象一样:

    let subject = PublishSubject<String>()
    
    

    其中PublishSubject的泛型参数,表示它可以订阅到的,以及可以发布的事件类型。

    其次,当我们把subject当作Observer的时候,可以使用onNext方法给它发送事件:

    subject.onNext("Episode1 updated")
    
    

    第三,当我们把subject当作Observable的时候,订阅它的代码和订阅普通的Observable完全一样:

    let sub1 = subject.subscribe(onNext: {
        print("Sub1 - what happened: \($0)")
    })
    
    

    但是执行一下就会发现,控制台上不会显示任何订阅消息,也就是说sub1没有订阅到任何内容。这是因为PublishSubject执行的是“会员制”,它只会把最新的消息通知给消息发生之前的订阅者。用序列图表示出来,就是这样的:

    image

    可以看到,在红灯之前订阅,就可以订阅到红、绿、蓝全部事件,如果在蓝灯之前订阅,就只能订阅到蓝色事件了。于是,为了订阅到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 updatedSub2 - what happened: Episode3 updated这两条消息;
    • 最后,sub2取消对subject的订阅;

    重新执行一下,就能在控制台看到结果了。

    BehaviorSubject

    如果你希望Subject从“会员制”变成“试用制”,就需要使用BehaviorSubject。它和PublisherSubject唯一的区别,就是只要有人订阅,它就会向订阅者发送最新的一次事件作为“试用”。

    image

    如图所示,BehaviorSubject带有一个紫灯作为默认消息,当红灯之前订阅时,就会收到紫色及以后的所有消息。而在绿灯之后订阅,就只会收到绿灯及以后的所有消息了。因此,当初始化一个BehaviorSubject对象的时候,要给它指定一个默认的推送消息:

    let subject = BehaviorSubject<String>(
        value: "RxSwift step by step")
    
    

    然后,当我们再执行先订阅,后发送消息的逻辑时:

    let sub1 = subject.subscribe(onNext: {
        print("Sub1 - what happened: \($0)")
    })
    
    subject.onNext("Episode1 updated")
    
    

    由于BehaviorSubject有了一个默认的事件,sub1订阅之后,就会陆续收到RxSwift step by stepSub1 - 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自带一个缓冲区,当有订阅者订阅的时候,它会向订阅者发送缓冲区内的所有消息;
    image

    ReplaySubject缓冲区的大小,是在创建的时候确定的:

    let subject = ReplaySubject<String>.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实现一些之前常见的开发任务。

    相关文章

      网友评论

        本文标题:16.5四种Subject的基本用法

        本文链接:https://www.haomeiwen.com/subject/obpydqtx.html