简介
本篇重点在于深入RxSwift的部分常用特性,所以希望读者在了解RxSwift官方的基本讲解与Demo之后再进行阅读。
RxSwift版本为5.0.0以上。
Dispose
在写代码的时候,我们经常会在很多情况下创建一个Observable
:
_ = Observable<String>.create { observerOfString -> Disposable in
observerOfString.on(.next("😬"))
observerOfString.on(.completed)
return Disposables.create()
}
又或者是在监听Observable
:
let disposeBag = DisposeBag()
Observable<Int>.empty()
.subscribe { event in
print(event)
}
.disposed(by: disposeBag)
这里总是看到是Disposables
和DisposeBag
,那么它们到底是什么。
Disposables
Disposables
是一个简单的结构体
public struct Disposables {
private init() {}
}
通过extension生成create方法, 创建四种不同的Disposable。
Disposable定义:
public protocol Disposable {
/// Dispose resource.
func dispose()
}
Cancelable
, 在Disposable
的基础上添加了isDisposed
属性,便于追踪Disposable
的状态:
public protocol Cancelable : Disposable {
/// Was resource disposed.
var isDisposed: Bool { get }
}
NopDisposable
, 在disposal时什么都不做。AnonymousDisposable
, 创建时传入一个public typealias DisposeAction = () -> Void
类型的闭包,在disposal时调用。BinaryDisposable
, 在创建时传入两个Disposable
类型的参数,在disposal时调用。CompositeDisposable
, 在创建时传入多个Disposable
类型的参数,在disposal时调用。
除了这四种可以通过快速创建的,还有其他类型的Disposable
BooleanDisposable
,主要用于追踪disposal的状态,初始化时传入Bool类型的参数,表示是否已经被Dispose。SubscriptionDisposable
,主要在Subject中使用。RefCountDisposable
,初始化时传入一个Disposable
类型的参数,如同命名,内存采用了引用计数的管理方法,调用retain方式时计数+1,调用release时计数-1,引用计数不能小于0,当等于0时,如果没有调用过dispose方法(其实是将内部的_primaryDisposed
属性标记为true
),也不会触发dispose方法,同样如果引用计数不为0,调用dispose方法也不会触发内部的Disposable
的dispose。ScheduledDisposable
,初始化时传入一个Disposable
类型的参数与一个ImmediateSchedulerType
,指定传入的Disposable
在某个线程上执行dispose。SerialDisposable
: 可以手动替换内部的Disposable
,如果在_isDisposed
为false的状态,替换后会自动触发之前的Disposable
的dispose方法,如果为true,则直接触发替换的Disposable
的dispose方法。SingleAssignmentDisposable
:对内部的Disposable
只能设定一次,若设定多次会报错,在没有设定的情况下触发dispose方法也会报错。
DisposeBag
DisposeBag
如同名字,是一个Bag
类型的数据结构,里面存放Disposable
的数据。
每次看到Disposable类型调用disposed(by: disposeBag)
,其实是将Disposable放在DisposeBag中进行管理,当DisposeBag进行dispose时,对其中管理的Disposable分别dispose,但是DisposeBag不能主动调用,只能在deinit时自动释放,所以想要进行dispose操作,只能将disposeBag重新赋值,比如:
disposeBag = DisposeBag()
Subject
简介
Subject是在RX的某些实现中可用的桥接或代理。
通过继承了ObserverType
,可以成为一个观察者,可以通过on(_ event: Event<Element>)
发送事件。
通过继承了Observable
,可以成为一个被观察者,可以通过subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element
推送事件。
Subject都是通过Broadcasts
的方式传播事件。
冷热信号
在RAC中通过Signal与SignalProducer区分冷热信号的概念,RxSwift其实也有冷热信号。
冷信号是指在被观察之后才会推送事件。
热型号是指就算没有被观察,也能推送事件。
可以理解为一个主动一个被动。
这是一个比较抽象的例子:
let subject = Subject()
subject.onNext("1")
subject.subcribeNext { value in
print(value)
}
subject.onNext("2")
如果是冷信号,这时候会打印1 2,热信号则只会打印2.
各种Subject
在Rx中有四种Subject。分别是BehaviorSubject
,PublishSubject
,ReplaySubject
,AsyncSubject
。
下列图标中,圆代表next信号,竖线表示complete,x表示error。
PublishSubject
就是一个热信号的Subject。
BehaviorSubject
是一个有初始值,缓存数量为1的Subject。在5.0版本之前的RxSwift中,有一个叫做Variable
的属性,在5.0之后,因为相同的特性,完全被BehaviorSubject
取代。
ReplaySubject
是一个可以自定义缓存数量的Subject。当设定缓存数量为0时,几乎可以当做PublishSubject
使用,缓存数量为1时,可以当做一个没有初始值的BehaviorSubject
使用。
AsyncSubject
,只会在接收到Complete事件后,才会Subscribe最后一个信号,如果没有在onComplete之前没有onNext事件,或者触发了onError,则不会触发Subscribe。
Demo
简单的按钮处理,当用户名长度大于4位且密码长度大于6位时,登录按钮才能点击:
private var disposeBag = DisposeBag()
private let nameSubject = BehaviorSubject<String>(value: "")
private let passwordSubject = BehaviorSubject<String>(value: "")
private let loginSubject = BehaviorSubject<Bool>(value: false)
private let nameTextField = UITextField()
private let passwordTextField = UITextField()
private let loginButton = UIButton()
......
//中间内容省略
......
nameTextField.rx.text.orEmpty.bind(to: nameSubject).disposed(by: disposeBag)
passwordTextField.rx.text.orEmpty.bind(to: passwordSubject).disposed(by: disposeBag)
Observable<Bool>.combineLatest(nameSubject, passwordSubject) { (name, password) -> Bool in
return name.count > 4 && password.count > 6
}.bind(to: loginButton.rx.isEnabled).disposed(by: disposeBag)
通过某个实时持续的交互,每秒刷新UI:
private let socketSubject = ReplaySubject<String>.create(bufferSize: 1)
......
DoSomething {
socketSubject.onNext(value)
}
Observable<Int>
.interval(.seconds(1), scheduler: MainScheduler.instance)
.withLatestFrom(socketSubject)
.distinctUntilChanged()
.subscribe(onNext: { (value) in
print(value)
}).disposed(by: disposeBag)
思考
如果ReplaySubject
的bufferSize为1,是否与BehaviorSubject
相同?
如果ReplaySubject
的bufferSize为0,是否与PublishSubject
相同?
Schedulers
Schedulers是RxSwift中的调度机制。
主要运算符只有两个observeOn
以及subscribeOn
。
sequence1
.observeOn(backgroundScheduler)
.map { n in
print("This is performed on the background scheduler")
}
.observeOn(MainScheduler.instance)
.map { n in
print("This is performed on the main scheduler")
}
.subscribeOn(subscribeScheduler)
.subscribe { _ in
print("This is performed on the subscribeScheduler")
}
通过observeOn
与subscribeOn
可以控制处理信号的线程,并且可以支持多次切换。
各种Scheduler
CurrentThreadScheduler
(串行)。指代当前线程,若没有指定Schuduler,则默认使用。MainScheduler
(串行)。主线程,通常使用进行UI操作。在subscribeOn时更应该用做过优化的ConcurrentMainScheduler
。SerialDispatchQueueScheduler
(串行)。串行线程,主线程也是一种串行线程。ConcurrentDispatchQueueScheduler
(并发)。在初始化时也可以传入串行dispatch queue,也不会有任何问题。适用于需要在后台的工作。OperationQueueScheduler
(并发)。是NSOperationQueue
的一种抽象示例。适用与单个任务量大的任务并行处理的情况,同时希望设定maxConcurrentOperationCount
。
思考
- 为什么说在subscribeOn时更应该用做过优化的
ConcurrentMainScheduler
? - 在阅读文档时,经常看到在observeOn时,如果指定了使用
SerialDispatchQueueScheduler
,会有所优化,具体是怎么优化的?
对于第二个问题,在阅读代码时,发现在observeOn
时做了单独的处理
public func observeOn(_ scheduler: ImmediateSchedulerType)
-> Observable<Element> {
if let scheduler = scheduler as? SerialDispatchQueueScheduler {
return ObserveOnSerialDispatchQueue(source: self.asObservable(), scheduler: scheduler)
}
else {
return ObserveOn(source: self.asObservable(), scheduler: scheduler)
}
}
对于非SerialDispatchQueueScheduler
,会通过将事件以队列的方式以及加递归锁的方式存储。
而SerialDispatchQueueScheduler
,则不会,减少了性能消耗。