本篇重點在於深刻RxSwift的部分經常使用特性,因此但願讀者在瞭解RxSwift官方的基本講解與Demo以後再進行閱讀。swift
RxSwift版本爲5.0.0以上。緩存
在寫代碼的時候,咱們常常會在不少狀況下建立一個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
是一個簡單的結構體socket
public struct Disposables {
private init() {}
}
複製代碼
經過extension生成create方法, 建立四種不一樣的Disposable。性能
Disposable定義:優化
public protocol Disposable {
/// Dispose resource.
func dispose()
}
複製代碼
Cancelable
, 在Disposable
的基礎上添加了isDisposed
屬性,便於追蹤Disposable
的狀態:ui
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
spa
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
如同名字,是一個Bag
類型的數據結構,裏面存放Disposable
的數據。
每次看到Disposable類型調用disposed(by: disposeBag)
,實際上是將Disposable放在DisposeBag中進行管理,當DisposeBag進行dispose時,對其中管理的Disposable分別dispose,可是DisposeBag不能主動調用,只能在deinit時自動釋放,因此想要進行dispose操做,只能將disposeBag從新賦值,好比:
disposeBag = DisposeBag()
複製代碼
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.
複製代碼
在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。
簡單的按鈕處理,當用戶名長度大於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是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
能夠控制處理信號的線程,而且能夠支持屢次切換。
CurrentThreadScheduler
(串行)。指代當前線程,若沒有指定Schuduler,則默認使用。MainScheduler
(串行)。主線程,一般使用進行UI操做。在subscribeOn時更應該用作過優化的ConcurrentMainScheduler
。SerialDispatchQueueScheduler
(串行)。串行線程,主線程也是一種串行線程。ConcurrentDispatchQueueScheduler
(併發)。在初始化時也能夠傳入串行dispatch queue,也不會有任何問題。適用於須要在後臺的工做。OperationQueueScheduler
(併發)。是NSOperationQueue
的一種抽象示例。適用與單個任務量大的任務並行處理的狀況,同時但願設定maxConcurrentOperationCount
。ConcurrentMainScheduler
?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
,則不會,減小了性能消耗。