博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RxSwift 之 Subject
阅读量:6809 次
发布时间:2019-06-26

本文共 3884 字,大约阅读时间需要 12 分钟。

Cover

上一篇介绍了 Observable 的基本概念和使用情形。但是大多数情形下,我们需要在应用运行时添加数据到 Observable 中并将其发送给订阅者。在这种需求场景下,我们就不得不使用 RxSwift 中另一种类型对象了- Subject 。

在应用中 Subject 实际上同时扮演了两个不同的角色:既是可观察对象同时也是观察者。这意味着 Subject 实例对象既可以接收事件也可以发送事件。例如,Subject 实例对象可以接收 next 事件信息,然后再将其发送给它自己的订阅者。示例代码:

let subject = PublishSubject
()let subscriptionOne = subject.subscribe(onNext: { string in print(string)})subject.on(.next("1"))/* 打印结果:1*/复制代码

上面代码中使用的是 PublishSubject 类型的示例,而 RxSwift 中总共也四种类型的 Subject:

  1. PublishSubject:初始化时并不包含数据,并且只会给订阅者发送后续数据。
  2. BehaviorSubject:创建时需要包含初始数据,并且会给订阅者发送后续数据和最近的一次数据。
  3. ReplaySubject:创建时需要指定对象缓存区容量,该容量表示会给订阅者重新发送订阅前数据的大小。
  4. Variable:BehaviorSubject 对象的封装类型。它会将当前数据保存为状态并且只会给订阅者重新发送最近或者初始值。

下面将详细介绍这四种类型对象的概念以及它们的区别和使用情况。

PublishSubject

如果你仅仅是想让订阅者获取被观察者在生命周期内若产生的数据的话,那么你完全可以选用 PublishSubject 。而且 PublishSubject 对象的行为符合正常的预期,它只会给订阅者发送其订阅开始之后的数据。

例如,下图的最上面的时间线表示被观察者所发送的事件,而下面两个则分别代表不同的观察者。可以看到下面两个观察者都只会接收到订阅后所发送的事件而无法获知之前的情形。

01

对应的代码为:

let subject = PublishSubject
()let subscriptionOne = subject.subscribe(onNext: { event in print("1) \( event.element ?? event)" )})subject.on(.next("1")) let subscriptionTwo = subject.subscribe(onNext: { event in print("2) \(event.element ?? event)")})subject.on(.next("2")) subject.on(.next("3")) /* 打印结果1) 11) 22) 21) 32) 3*/复制代码

如果此时我们取消 subscriptionOne 的订阅并发送新数据的话,那么结果为:

subscriptionOne.dispose()subject.on(.next("4"))   /* 打印结果 2) 4 */复制代码

另外,当 PublishSubject 对象生命周期结束时,无论后续是否继续有数据产生该对象只会简单的发送之前结束生命周期的事件。

// 结束生命周期subject.onCompleted()// 发送新数据subject.onNext("5")// 结束观察subscriptionTwo.dispose()let disposeBag = DisposeBag()// 重新进行订阅操作 subject.subscribe { print("3)", $0.element ?? $0) } .addDisposableTo(disposeBag)// 发送新数据subject.onNext("?")/* 打印结果2) completed3) completed */复制代码

对于时序敏感的操作来说,PublishSubject 显然是非常合适的选择。但是并不是所有的情形都时序敏感,有时候我们可能会希望在订阅时能够获知最近一次的数据。此时,我们就需要使用 BehaviorSubject 对象了。

BehaviorSubject

BehaviorSubject 的行为与 PublishSubject 几乎一致,不过前者会给订阅者多发送一个最近的数据。图解如下:

02

图示中最上面对应的是所发射的数据,其中第二行表示第一个观察者,第三行则表示另一个。可以发现第一个观察者是在 1 之后 2 之前进行观察的,但是它依然能够获取到数据 1 。我们可以通过代码进行验证:

let subject = BehaviorSubject(value: "1")let bag = DisposeBag()subject.subscribe { event inprint("1) event: \(event.element!) ")}.addDisposableTo(bag)subject.subscribe { event inprint("2) event: \(event.element!) ")}.addDisposableTo(bag)subject.onNext("2")subject.subscribe { event inprint("3) event: \(event.element!) ")}.addDisposableTo(bag)subject.onNext("3")复制代码

因为始终都能获取到最近的数据,所以对于那些可能需要默认值的场景,BehaviorSubject 显然是一个好的选择。

ReplaySubject

ReplaySubject 与 BehaviorSubject 的行为非常接近,只不过前者允许订阅者获取多于 1 个的最近数据。所以从某种意义上来说,后者是前者的一个特例。

下图就是一个 buffer 大小为 2 的 ReplaySubject 对象。它总过发射了三次数据,其中第一个观察者获取了所以的数据。而第二个观察者虽然是在第二个数据发射后才开始,但它依旧能获取缓存区中保存的数据。

03

代码表示如下:

let subject = ReplaySubject
.create(bufferSize: 2)let bag = DisposeBag()subject .subscribe { event in print("1) event: \(event.element!) ") } .addDisposableTo(bag)subject.onNext("1")subject.onNext("2")subject .subscribe { event in print("2) event: \(event.element!) ") } .addDisposableTo(bag)subject.onNext("3")/* 打印结果:1) event: 1 1) event: 2 2) event: 1 2) event: 2 1) event: 3 2) event: 3 */复制代码

不过有一点值得我们注意,因为 ReplaySubject 缓存机制使用了数组结构,所以当有大量 ReplaySubject 对象时可能导致内存暴增。另外,如果缓存对象是图片等极耗内存的资源时也可能导致内存问题。所以 ReplaySubject 不可滥用且缓存区大小应该合理进行设置。

Variable

前面提到过 Variable 类型是 BehaviorSubject 的封装类型,从某种意义上你可以将前者当作后者的子类(实际上并不是)。Variable 类型实例的行为与 BehaviorSubject 一致,只不过前者添加了一些自有特性。你可以通过 value 属性访问和设置 Variable 类型实例当前的状态值,这意味着我们无需调用 onNext(_:) 了。

作为 BehaviorSubject 封装后的类型,Variable 在初始化时也需要设置默认值。另外,它发送数据的行为也与 BehaviorSubject 一致:只会给订阅者重新发送最近或者初始值。另一个独有的特性是,Variable 实例是不会触发 error 事件的。也就是说,你可以订阅 Variable 实例的错误事件,但是你并不能添加一个错误事件到实例中。

代码示例:

var variable = Variable("Initial value")let bag = DisposeBag()variable.value = "New initial value"variable.asObservable()    .subscribe { event in        print("1) event: \(event.element!) ")    }    .addDisposableTo(bag)复制代码

原文

转载地址:http://gmqwl.baihongyu.com/

你可能感兴趣的文章
读《王垠-天才是什么》有感
查看>>
MySql中PreparedStatement对象与Statement对象
查看>>
Unity3D使用经验总结 编辑器扩展篇
查看>>
openjudge 7622 求排列的逆序数(归并)
查看>>
利用express启一个server服务
查看>>
C#对IE使用Proxy(代理)
查看>>
sign签名算法一致算法-.net、java、golang
查看>>
Node.js简介与架构
查看>>
Entity Framework 异常档案
查看>>
如何成功发布一个MSMQ的Windows服务
查看>>
EntLib 3.1学习笔记(5) : Exception Handling Application Block
查看>>
工厂模式 接口 封装 实例
查看>>
bzoj1061 志愿者招募
查看>>
p2093 [国家集训队]JZPFAR
查看>>
枚举当前打开的所有窗口
查看>>
【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)...
查看>>
GDI+中发生一般性错误的解决办法(转)
查看>>
【Selenium】1.介绍 Selenium
查看>>
wxpython仿写记事本
查看>>
MM 常用表
查看>>