RxSwift 之 Subject

Cover
Cover

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

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

let subject = PublishSubject<String>()

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 对象的行为符合正常的预期,它只会给订阅者发送其订阅开始以后的数据。ui

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

01
01

对应的代码为:3d

let subject = PublishSubject<String>()

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) 1 1) 2 2) 2 1) 3 2) 3 */复制代码

若是此时咱们取消 subscriptionOne 的订阅并发送新数据的话,那么结果为:code

subscriptionOne.dispose()
subject.on(.next("4"))  

 /* 打印结果 2) 4 */复制代码

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

// 结束生命周期
subject.onCompleted()

// 发送新数据
subject.onNext("5")

// 结束观察
subscriptionTwo.dispose()

let disposeBag = DisposeBag()
// 从新进行订阅操做 
subject
.subscribe { 
print("3)", $0.element ?? $0) 
} 
.addDisposableTo(disposeBag)
// 发送新数据
subject.onNext("?")

/* 打印结果 2) completed 3) completed */复制代码

对于时序敏感的操做来讲,PublishSubject 显然是很是合适的选择。可是并非全部的情形都时序敏感,有时候咱们可能会但愿在订阅时可以获知最近一次的数据。此时,咱们就须要使用 BehaviorSubject 对象了。htm

BehaviorSubject

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

02
02

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

let subject = BehaviorSubject(value: "1")
let bag = DisposeBag()

subject
.subscribe { event in
print("1) event: \(event.element!) ")
}
.addDisposableTo(bag)

subject
.subscribe { event in
print("2) event: \(event.element!) ")
}
.addDisposableTo(bag)

subject.onNext("2")

subject
.subscribe { event in
print("3) event: \(event.element!) ")
}
.addDisposableTo(bag)

subject.onNext("3")复制代码

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

ReplaySubject

ReplaySubject 与 BehaviorSubject 的行为很是接近,只不过前者容许订阅者获取多于 1 个的最近数据。因此从某种意义上来讲,后者是前者的一个特例。

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

03
03

代码表示以下:

let subject = ReplaySubject<String>.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)复制代码

原文地址

相关文章
相关标签/搜索