RxSwift的目的是以可观察对象的形式轻松组合异步操作和数据流,并使用一套方法来转换和组合这些异步工作。
KVO观察、异步操作、UI事件和其他数据流都在序列抽象下统一。
Observable何时开始发布其项目序列?
这取决于可观测的。“热门”Observable可能会在创建后立即开始发射项目,因此任何后来订阅该Observable的观察者都可能会在在中间的某个位置开始观察序列。另一方面,一个“冷”可观察器会等到观察者订阅它之后才开始发射项目,因此这样的观察者可以从一开始就看到整个序列。
冷热观察序列对比
热 Obseavables | 冷 Observables |
---|---|
是序列 | 是序列 |
无论是否有观察员订阅,都要使用资源(“产生热量”) | 在观察者订阅之前不使用资源(不要产生热量)。 |
变量/属性/常量、点击坐标、鼠标坐标、UI控件值、当前时间 | 异步操作、HTTP连接、TCP连接、流 |
通常含有~N个元素 | 通常包含~1个元素 |
无论是否有观察者订阅,都会生成序元素 | 只有当观察者订阅时,才会生成序列元素 |
序列计算资源通常在所有订阅的观察器之间共享 | 序列计算资源通常被分配给每一个观察者 |
通常有状态 | 通常没状态 |
检测内存是否泄漏
在 AppDelegate 的 didFinishLaunchingWithOptions
方法中添加如下代码 :
_ = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
.subscribe(onNext: { _ in
print("Resource count \(RxSwift.Resources.total)")
})
接下来编译时, 会报错发现不了 Resources 属性。 在 Podfile 中添加如下代码
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == 'RxSwift'
target.build_configurations.each do |config|
if config.name == 'Debug'
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['-D', 'TRACE_RESOURCES']
end
end
end
end
end
如果还报错, 对比 Pods -> Build Settings -> Other Swift Flags
如果不是, 添加相应的 -D DEBUG
再次编译成功!
内存是否泄漏,在订阅开始和订阅取消之后, 查看 RxSwift.Resources.total
是否变化
操作符
amb
当有多个 Observales 被添加到 amb 操作符时
- 如果其中有 Observables 是 error、complete 等事件, 则只发出该 Observables 的事件
- 如果其中都是正常的发出元素事件, 则只发出第一个 Observables 的元素。
let disposeBag = DisposeBag()
let observalbles1 = Observable.from([20, 40, 60])
let observalbles2 = Observable.from([1, 2, 3])
let observalbles3 = Observable<Int>.never()
let observalbles4 = Observable<Int>.empty()
let observalbles5 = Observable<Int>.error(GenerError.generic)
let sequences = [observalbles1, observalbles2, observalbles3]
Observable.amb(sequences)
.subscribe { e in
print("subscription 1 event: \(e)")
}
.disposed(by: disposeBag)
buffer
缓存指定数量的元素时才一起发出, 或者在指定时间内发出元素, 超过指定时间, 有几个发几个, 没有就发出空数组。
//每缓存三个元素,则组合起来一起发出
//如果1秒钟内不够3个也会发出(有几个发几个, 一个没有就发送空数组)
subject.buffer(timeSpan: .seconds(1), count: 3, scheduler: MainScheduler.instance)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
subject.onNext("1")
subject.onNext("2")
subject.onNext("3")
subject.onNext("A")
subject.onNext("B")
subject.onNext("C")
catch
拦截 error 事件, 将替换成一其他元素或者一组元素。
let subjectWithFail = PublishSubject<Int>()
let replaceSubject = PublishSubject<Int>()
subjectWithFail
.catch({ e in
print("error = \(e)")
return replaceSubject
})
.subscribe {
print($0)
}
.disposed(by: disposeBag)
subjectWithFail.onNext(1)
subjectWithFail.onError(GenerError.generic)
replaceSubject.onNext(2)
catchAndReturn
拦截error 事件, 转换为一个元素。
let subjectWithFail = PublishSubject<Int>()
subjectWithFail
.catchAndReturn(2)
.subscribe {
print($0)
}
.disposed(by: disposeBag)
subjectWithFail.onNext(1)
subjectWithFail.onError(GenerError.generic)
combineLatest
将多个 Observables 组合起来, 当有其中一个 Observables 发出元素, 则组合将发出一个元素, 组合的规则: 拿各个Observables 中最新的元素, 如图:
let first = PublishSubject<String>()
let second = PublishSubject<String>()
Observable.combineLatest(first, second) { $0 + $1 }
.subscribe {
print($0.element ?? "")
}
.disposed(by: disposeBag)
first.onNext("1")
second.onNext("A")
first.onNext("2")
second.onNext("B")
second.onNext("C")
second.onNext("D")
first.onNext("3")
first.onNext("4")
first.onNext("5")
concat
连接两个或多个Observables, 按顺序串联起来, 前一个 Observables 事件发送没完成, 后一个Observables 不会发出元素。
let subject1 = BehaviorSubject(value: "1")
let subject2 = BehaviorSubject(value: "A")
let subject = BehaviorSubject(value: subject1)
subject
.asObservable()
.concat()
.subscribe { print($0) }
.disposed(by: disposeBag)
subject1.onNext("2")
subject1.onNext("3")
subject.onNext(subject2)
subject2.onNext("I would be ignored") // subject1 的事件序列没结束, 所以这个会被忽略
subject2.onNext("B")
subject1.onCompleted()
subject2.onNext("C") // 在接收 C 之前会先接收 B, 因为是 BehaviorSubject
concatMap
对比 concat 多了一个 map 操作, 就是将序列元素转换为其他类型元素。
connect & publish & refCount
其中 publish
是将 Observable
类型 转为 ConnnetableObservable
类型。在被订阅后, 不会发出元素, 直到被应用connect
为止。可以等所有观察者订阅完成之后,再connect。
let intSequence = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
.publish()
intSequence
.subscribe {
print("suscription 1 \($0)")
}
.disposed(by: disposeBag)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
intSequence.connect()
.disposed(by: self.disposeBag)
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 4) {
intSequence
.subscribe {
print("suscription 2 \($0)")
}
.disposed(by: self.disposeBag)
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 4) {
intSequence
.subscribe {
print("suscription 3 \($0)")
}
.disposed(by: self.disposeBag)
}
- publish 将 Observable 转换为 可连接的 Observable
- refCount 将 可连接的 Observable 转换为 Observable
- replay(n: Int) 和 publish 的区别是, 会缓存 n 个最新的元素。
Observable.of(1, 2, 3)
.publish()
.refCount()
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
Throttle(节流) & Debounce(防抖)
Throttle
让一个函数调用在规定时间内只能执行一次调用, 不能频繁调用, 影响性能。只有过了规定时间间隔, 才执行下一次函数调用。
例如咖啡机, 按一次按钮之后, 连续再次按动按钮, 不再起作用。直到一杯咖啡接满为止, 再次按动按钮, 才会响应
Debounce
触发函数调用, 在n秒后执行, 如果在n秒之内再次触发函数调用,将重新计算, 再延迟 n 秒之后执行函数。
例如电梯的自动关门动作, 20秒后关闭, 如果20秒内有人进入, 重新计算,直到没人再进入为止。
deferred & delay
deferred
直到订阅才产生序列, 为每一个订阅者创建全新的序列。
let obs = Observable.deferred {
Observable.of(1,2,3)
}
obs.subscribe { e in
print(e.element ?? 0)
}
.disposed(by: disposeBag)
delay 将序列的元素, 延迟一段时间后发出, 不是间隔发送一个个的元素, 而是延迟到规定的时间,一次发出所有元素。
let obs = Observable.of(1,2,3)
.delay(.seconds(5), scheduler: MainScheduler.instance)
obs
.subscribe { e in
print(e.element ?? 0)
}
.disposed(by: disposeBag)
materialize & dematerialize
materialize
将事件转换为元素
dematerialize
将上面的转换过程还原。
do
在实际的编程中,有时我们会串联多个operator对事件序列进行处理,虽然这样写起来很方便,但发生问题调试时就很麻烦了,因为紧密串联在一起的代码让我们很难方便的洞察每一个环节的状态。为此,RxSwift提供了一个类似“旁路”功能的operator:do。它的用法和subscribe类似,只不过事件会“穿过”do,继续发给后续的环节。这样,如果我们怀疑某个串联的环节发生了问题,就可以插入一个do operator进行观察
let first = BehaviorSubject(value: "1")
let subject = BehaviorSubject(value: first)
subject
.do(onNext: { subject in
print("subject: \(subject)")
})
.flatMap { $0 }
.do(onNext: { element in
print("element: \(element)")
})
.subscribe { e in
print(e)
}
.disposed(by: disposeBag)
flatMap
当 Observable 的元素本身有其他的 Observables 时, 你可以将所有的子Observables 的元素发出。
let first = BehaviorSubject(value: "1")
let second = BehaviorSubject(value: "A")
let subject = BehaviorSubject(value: first)
subject
.flatMap { $0 }
.subscribe { e in
print(e)
}
.disposed(by: disposeBag)
first.onNext("2")
first.onNext("3")
subject.onNext(second)
second.onNext("B")
second.onNext("C")
flatMapLatest
将 Observable 的元素转换为 Observables, 取最新的被转换的那个Observale 下的所有子元素。
let disposeBag = DisposeBag()
let first = BehaviorSubject(value: "1")
let second = BehaviorSubject(value: "A")
let subject = BehaviorSubject(value: first)
subject.asObservable()
.flatMapLatest { $0 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
first.onNext("2")
subject.onNext(second)
second.onNext("B")
first.onNext("3") // 旧的 Observables 被忽略掉
From
- 将一个数组转换为 Observale
- 将一个可选值转换为 Observable
let optional: Int? = 1
let obs = Observable.from(optional: optional)
obs.subscribe {
print($0)
}
.disposed(by: disposeBag)
相当于
let optional: Int? = 1
let obs = Observable<Int>.create { observer in
if let element = optional {
observer.onNext(element)
}
observer.onCompleted()
return Disposables.create()
}
groupBy
将 Observable 的元素分组后, 再发出。
let nums = [1, 2, 3, 4, 5, 6, 7, 8]
Observable.from(nums)
.groupBy {
$0 % 2 == 0 ? "偶数" : "奇数"
}
.subscribe { e in
guard let key = e.element?.key else { return }
e.element?.asObservable()
.subscribe { e in
guard let value = e.element else { return }
print("key = \(key), value = \(value)")
}
.disposed(by: self.disposeBag)
}
.disposed(by: disposeBag)
interval
每隔一段时间, 发出一个索引数
ignoreElements
忽略掉所有元素, 只发出error 和 complete 事件。 如果只关注事件什么时候终止, 可以用这个。
merge
将多个 Observable 合并成一个, 当某一个Observable 发出一个元素时, 它也发出这个元素。
let first = PublishSubject<String>()
let second = PublishSubject<String>()
Observable.merge(first, second)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
first.onNext("1")
second.onNext("A")
second.onNext("B")
first.onNext("2")
Never
不会发出任何事件, 注意和 empty(只发出完成事件) 的区分。
observeOn & su subscribeOn
- observeOn 指定 Observable 在那个
Scheduler
发出通知。 - subscribeOn 指定 Observable 在那个
Scheduler
执行。
注意⚠️, 一旦产生 error 事件, 不会等待之前发出的元素接收完毕, 这意味着 error 事件会比其他事件提前被订阅者=接收到。
reduce & scan
- reduce 将所有的元素应用某一个函数, 算出最终结果后发出元素,只发出一个元素
Observable.of(1, 2, 3)
.reduce(0) { result, value in
result + value
}
// .reduce(0, accumulator: +)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
// 6
- scan 将所有的元素应用某一个函数, 将每一次的计算结果都输出,发出元素个数不变
Observable.of(1, 2, 3)
.scan(0) { $0 + $1 }
// .scan(0, accumulator: +)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
repeatElement
无止境的发出同一个元素。
Observable.repeatElement(1)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
retry
产生错误时, 将不会发送error 事件, 而是将重新生成序列再发送元素, 这就可能导致, 错误产生之前的元素, 重复发送一次。
var count = 1
let obsWithError = Observable<String>.create { observer in
observer.onNext("A")
observer.onNext("B")
observer.onNext("C")
if count == 1 {
print("error produce")
observer.onError(GenerError.generic)
count += 1
}
observer.onNext("E")
observer.onNext("F")
observer.onNext("G")
return Disposables.create()
}
obsWithError
.retry()
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
sample
对 Observable 取样, 通过第二个 Observable 发出元素的时机对 源 Observable 取样。
let orignal = PublishSubject<Int>()
let second = PublishSubject<String>()
orignal.sample(second, defaultValue: 0)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
second.onNext("A")
orignal.onNext(1)
orignal.onNext(2)
second.onNext("B")
// 0 2
shareReplay
使观察着共享 Obsevervable, 会接收到最新的元素。即使是在订阅前产生的。buffSize 缓存个数
skipUntil & takeUntil
skipUntil
跳过头几个元素, 直到另一个 Observable发出元素。
let sourceObs = PublishSubject<String>()
let referenceObs = PublishSubject<Int>()
sourceObs
.skip(until: referenceObs)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
sourceObs.onNext("A")
sourceObs.onNext("B")
referenceObs.onNext(1)
referenceObs.onNext(2)
sourceObs.onNext("C")
// C
takeUntil
发出元素, 直到另一个Observable 发出元素, 后面的将忽略。
skipWhile & takeWhile
skipWhile
跳过头几个元素, 直到判定条件为 falsetakeWhile
取头几个元素, 直到判定条件为 false
timeout
在固定时间内没有发出任何元素, 将产生 error 事件
let sourceObs = PublishSubject<String>()
let referenceObs = PublishSubject<Int>()
sourceObs
.timeout(.seconds(2), scheduler: MainScheduler.instance)
.subscribe {
print($0)
}
.disposed(by: disposeBag)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
sourceObs.onNext("A") // 发出
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
sourceObs.onNext("B") // 超时, 不发出
}
带 other 参数的
如果下一个元素没有在从其前一个元素开始的指定超时持续时间内接收到,则使用另一个可观察序列从那时起生成未来消息。
let sourceObs = PublishSubject<String>()
let referenceObs = PublishSubject<String>()
sourceObs
.timeout(.seconds(2), other: referenceObs, scheduler: MainScheduler.instance)
.subscribe {
print($0)
}
.disposed(by: disposeBag)
sourceObs.onNext("A")
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
referenceObs.onNext("1")
referenceObs.onNext("2")
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
sourceObs.onNext("B") // 不会发出, 已经被 referenceObs 替代
}
timer
在延迟一段时间后, 按间隔时间, 产生索引数
let rxTimer = Observable<Int>
.timer(.seconds(2), period: .seconds(1), scheduler: MainScheduler.instance)
// 2 秒后,每1秒产生索引数,period 不传的时候, 只产生索引0
let timerDisposeable = rxTimer
.subscribe {
print($0)
}
// .disposed(by: disposeBag) // 自动释放
DispatchQueue.main.asyncAfter(deadline: .now() + 8) {
timerDisposeable.dispose() // 手动暂停 timer
}
using
创建一个 Observable的 同时, 可以创建一个可被清除的资源。 Observable 事件终止时, 资源被清除。
_ = Observable<Int>.using({ () -> MyDisposable in
let dispose = MyDisposable()
print("创建source: \(dispose)")
return dispose
}, observableFactory: {
factory in
print("创建factory: \(factory)")
return Observable.of(1,2,3,4,5)
.debug("factory")
})
.debug("using")
.subscribe()
class MyDisposable: NSObject, Disposable {
func dispose() {
print("MyDisposable --- \(#function)")
}
deinit {
print("MyDisposable --- \(#function)")
}
}
window
buffer
周期性的将缓存的元素集合发送出来,而 window
周期性的将元素集合以 Observable
的形态发送出来。
buffer
要等到元素搜集完毕后,才会发出元素序列。而 window
可以实时发出元素序列。
Observable.of(1,2,3,4,5,6)
.window(timeSpan: .seconds(2), count: 2, scheduler: MainScheduler.instance)
.subscribe {
guard let interObs = $0.element?.asObservable() else { return }
interObs
.subscribe(onNext: {
print($0)
})
.disposed(by: self.disposeBag)
}
.disposed(by: disposeBag)
withLatestFrom
将两个 Observable 组合起来, 当第一个Observable 发出元素的时候, 取的是第二个Observable 最新的元素。
firstSubject
.withLatestFrom(secondSubject)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
firstSubject.onNext("🅰️")
firstSubject.onNext("🅱️")
secondSubject.onNext("1")
secondSubject.onNext("2")
firstSubject.onNext("🆎")
// 2
zip
将多个 Observable 组合起来, 严格按照每个序列的索引组合。
let obs1 = Observable.of("1", "2", "3")
let obs2 = Observable.of("A", "B")
Observable.zip(obs1, obs2) { $0 + $1 }
.subscribe(onNext: { e in
print(e)
})
.disposed(by: disposeBag)
参考链接: