最近在學習RxSwift相關的內容,在這裡記錄一些基本的知識點,以便今後查閱。
Observable
在RxSwift中,最關鍵的一個概念是可觀察序列(Observable Sequence),它相當於Swift中的序列(Sequence),可觀察序列中的每個元素都是一個事件,我們知道Swift的序列中可以包含任意多個元素,類似的,可觀察序列會不斷產生新的事件直到發生錯誤或正常結束為止。訂閱者(Observer)通過訂閱(subscribe)一個可觀察佇列來接收序列所產生的新事件,只有在有觀察者的情況下序列才可以傳送事件。
例如,使用of
操作建立一個可觀察序列:
let seq = Observable.of(1, 2, 3)
複製程式碼
of
是一種用來建立Observable的簡便操作,在上面的程式碼中建立了一個型別為Observable<Int>
的Observable,裡面包含了三個元素:1,2,3。
來看看Observable中都提供了哪些操作,可觀察序列是一個實現了ObservableType
協議的型別,ObservableType
協議的定義非常簡單:
protocol ObservableType : ObservableConvertibleType {
associatedtype E
func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E
}
複製程式碼
其中E
是一個關聯型別,表示序列中元素的型別,除此之外協議只定義了一個方法:subscribe
,用於向可觀察序列新增一個觀察者(ObserverType
型別):
// 接收閉包的subscribe函式是通過協議擴充套件提供的簡便方法
seq.subscribe { (event) in
print(event)
}
複製程式碼
subscribe
相當於Swift序列中的遍歷操作(makeIterator),如上,向seq序列新增一個觀察者,在序列中有新的事件時呼叫該閉包,上面的程式碼會輸出1,2,3。
Observer
觀察者是實現了ObserverType
協議的物件,ObserverType
協議同樣十分簡單:
public protocol ObserverType {
associatedtype E
func on(_ event: Event<E>)
}
複製程式碼
E
為觀察者所觀察序列中的元素型別,當序列中有新的事件產生時,會呼叫on
方法來接收新的事件。其中事件的型別Event
是一個列舉,其中包含3個型別:
enum Event<Element> {
case next(Element)
case error(Swift.Error)
case completed
}
複製程式碼
.next
:表示序列中產生了下一個事件,關聯值Element儲存了該事件的值。.error
:序列產生了一個錯誤,關聯值Error儲存了錯誤型別,在這之後序列會直接結束(不再產生新的next事件)。.completed
:序列正常結束。
Dispose
除了產生錯誤和自然結束以外,還可以手動結束觀察,在使用subscribe
訂閱一個可觀察序列時,會返回一個Disposable
型別的物件。這裡的Disposable
是一個協議,只定義了一個方法:
protocol Disposable {
func dispose()
}
複製程式碼
dispose
方法用來結束此次訂閱並釋放可觀察序列中的相關資源,通常來說你並不需要直接呼叫該方法,而是通過呼叫其擴充套件方法addDisposableTo
將Disposable
新增到一個DisposeBag
物件中。DisposeBag物件會自動管理所有新增到其中的Disposable物件,在DisposeBag物件銷燬的時候會自動呼叫其中所有Disposable的dispose方法釋放資源。
也可以使用takeUntil
來自動結束訂閱:
seq.takeUntil(otherSeq)
.subscribe({ (event) in
print(event)
})
複製程式碼
在otherSeq序列發出任意型別的事件之後,自動結束本次訂閱。
建立序列
通過Observable
型別提供的方法create
可以建立一個自定義的可觀察序列:
let seq = Observable<Int>.create { (observer) -> Disposable in
observer.on(.next(1))
observer.on(.completed)
return Disposables.create {
// do some cleanup
}
}
複製程式碼
create
方法使用一個閉包來建立自定義的序列,閉包接收一個ObserverType
的引數observer,並通過observer來傳送相應的事件。如上面的程式碼,建立了一個Observable<Int>
型別的可觀察序列,訂閱該序列的觀察者會收到事件1和一個完成事件。最後create
方法返回一個自己建立的Disposable
物件,可以在這裡進行一些相關的資源回收操作。
除了create
方法之外,RxSwift中提供了很多中簡便的方法用於建立序列,常用的有:
-
just:建立一個只包含一個值的可觀察序列:
let justSeq = Observable.just(1) justSeq.subscribe { (event) in print(event) } ---- example output ---- next(1) completed 複製程式碼
-
of:
of
和just
有點類似,不同的是of
可以將一系列元素建立成事件佇列,該Observable
依次傳送相應事件和結束事件:let ofSeq = Observable.of(1, 2, 3) ofSeq.subscribe { (event) in print(event) } ---- example output ---- next(1) next(2) next(3) completed 複製程式碼
-
empty:這種型別的Observable只傳送結束(Completed)事件
let emptySequence = Observable<String>.empty() 複製程式碼
-
error:該佇列只傳送一個
error
事件,傳遞一個自定義的錯誤型別。let errorSeq = Observable<TestError>.error(TestError.Error1) 複製程式碼
Share
通常在我們在訂閱一個可觀察序列的時候,每一次的訂閱行為都是獨立的,也就是說:
let seq = Observable.of(1, 2)
// 1
seq.subscribe { (event) in
print("sub 1: \(event)")
}
// 2
seq.subscribe { (event) in
print("sub 2: \(event)")
}
---- example output ----
sub 1: next(1)
sub 1: next(2)
sub 1: completed
sub 2: next(1)
sub 2: next(2)
sub 2: completed
複製程式碼
我們連續訂閱同一序列兩次,每次都會接收到相同的事件,第二次訂閱時並沒有因為第一次訂閱的行為導致元素"耗盡"。有些時候我們希望讓所有的觀察者都共享同一份事件,這個時候可以使用share
-
share:
share
是ObservableType
協議的一個擴充套件方法,它返回一個可觀察序列,該序列的所有觀察者都會共享同一份訂閱,上面的程式碼加上share之後:let seq = Observable.of(1, 2).share() // 1 seq.subscribe { (event) in print("sub 1: \(event)") } // 2 seq.subscribe { (event) in print("sub 2: \(event)") } ---- example output ---- sub 1: next(1) sub 1: next(2) sub 1: completed sub 2: completed 複製程式碼
可以看到,在第一次訂閱時序列已經將所有的事件傳送,後面再進行第二次訂閱的時候只收到了一個完成事件。
-
shareReplay:
shareReplay
的用法與share
類似,它的方法簽名如下:func shareReplay(_ bufferSize: Int) -> Observable<Element> 複製程式碼
不同的地方在於,
shareReplay
接收一個整型引數bufferSize,指定緩衝區大小,訂閱該序列的觀察者會立即收到最近bufferSize條事件。
序列的變換和組合
在Swift的序列Sequence
中,可以使用map、flatMap和reduce等常見的函式式方法對其中的元素進行變換,RxSwift中的可觀察序列同樣也支援這些方法。
變換
-
map:這是
map
方法的簽名:func map<Result>(_ transform: @escaping (E) throws -> Result) -> Observable<Result> 複製程式碼
在一個自定義的閉包中對序列的每一個元素進行變換,返回一個包含轉換後結果的可觀察序列,與Swift中
Sequence
的map類似。let mappedSeq: Observable<String> = seq.map { (element) -> String in return "value: \(element)" } 複製程式碼
-
flatMap:先來看看
flatMap
的簽名:func flatMap<O: ObservableConvertibleType>(_ selector: @escaping (E) throws -> O) -> Observable<O.E> 複製程式碼
關於flatMap的作用同樣可以類比
Sequence
,Sequence
中的flatMap
閉包遍歷每一個元素進行處理後返回一個新的序列,最後會將這些序列"展平",得到一個包含所有序列元素的新序列:let array = [1, 2] let res = array.flatMap { (n) -> [String] in return ["\(n)a", "\(n)b"] } // res: ["1a", "1b", "2a", "2b"] 複製程式碼
RxSwift中的
flatMap
用法與之類似,flatMap
中的閉包會遍歷可觀察序列中的所有元素,並返回一個新的可觀察序列,最後flatMap
會返回一個包含所有元素的可觀察序列:let seq = Observable.of(1, 2) .flatMap { (n) -> Observable<String> in return Observable.of("\(n)a", "\(n)b") // (1) } .subscribe { (event) in print(event) } // 得到的seq型別為Observable<String> ---- example output ---- next(1a) next(1b) next(2a) next(2b) completed 複製程式碼
在閉包中建立了若干個可觀察序列(1),這些序列中傳送的
next
事件都會被傳遞到seq序列中,其中任何一個序列發生錯誤(傳送了error
事件)時,seq序列都會直接結束,不再繼續接收事件;但是隻有所有序列都完成(傳送了completed
事件)後,seq序列才會正常結束。 -
flatMapLatest:作用與
flatMap
類似,但是對於閉包中生成的可觀察序列,它並不會保留所有的序列的訂閱,在遍歷結束後,只保留最後建立的序列的訂閱,之前建立的Observables都會取消訂閱(相應序列的dispose方法也會被呼叫):// 與上一個例子相同的程式碼,僅將flatMap改成flatMapLatest let seq = Observable.of(1, 2) .flatMapLatest { (n) -> Observable<String> in return Observable.of("\(n)a", "\(n)b") // (1) } .subscribe { (event) in print(event) } ---- example output ---- next(1a) next(2a) next(2b) completed 複製程式碼
因為訂閱關係的改變,現在只有當最後建立的那個Observable正常結束時,seq才會收到
completed
事件。在這種情況下,
flatMapLatest
會得到與flatMap
相同的輸出:let seq = Observable.of(1, 2) .flatMapLatest { (n) -> Observable<String> in return Observable<String>.create({ (observer) -> Disposable in observer.onNext("\(n)a") observer.onNext("\(n)b") return Disposables.create { } }) } .subscribe { (event) in print(event) } 複製程式碼
這是因為在上面的這個例子中所建立的Observable是同步建立元素的,無法被打斷。
類似的方法還有
flatMapFirst
,使用方法可以類比flatMapLatest
。 -
reduce和scan:
reduce
的作用與Sequence中定義的一樣,它接收一個初始值和一個閉包,在Observable中的每個值上呼叫該閉包,並將每一步的結果作為下一次呼叫的輸入:Observable.of(1, 2, 3).reduce(0) { (first, num) -> Float in return Float(first + num) } .subscribe { (event) in print(event) } // 輸出:next(6.0), completed 複製程式碼
在上面的程式碼中,提供了一個初始值0,在閉包中計算和,並將結果序列的元素型別改成
Float
,序列的觀察者最後接收到所有元素的和。scan
的作用類似於reduce
,它跟reduce
之間唯一的區別在於,scan
會傳送每一次呼叫閉包後的結果:Observable.of(1, 2, 3).scan(0) { (first, num) -> Float in return Float(first + num) } .subscribe { (event) in print(event) } // 輸出:next(1.0), next(3.0), next(6.0), completed 複製程式碼
組合
-
startWith:在序列的開頭加入一個指定的元素
Observable.of(2, 3).startWith(1).subscribe { (event) in print(event) } ---- example output ---- next(1) next(2) next(3) completed 複製程式碼
訂閱該序列之後,會立即收到
startWith
指定的事件,即使此時序列並沒有開始傳送事件。 -
merge:當你有多個型別相同的Observable,可以使用
merge
方法將它們合併起來,同時訂閱所有Observable中的事件:let seq1 = Observable.just(1) let seq2 = Observable.just(2) let seq = Observable.of(seq1, seq2).merge() seq.subscribe { (event) in print(event) } ---- example output ---- next(1) next(2) completed 複製程式碼
只有當Observable中的元素也是Observable型別的時候才可以使用
merge
方法,當其中一個序列發生錯誤的時候,seq都會被終止,同樣的只有所有序列都完成之後,seq才會收到完成事件。 -
zip:
zip
方法也可以將多個Observable合併在一起,與merge
不同的是,zip
提供了一個閉包用來對多個Observable中的元素進行組合變化,最後獲得一個新的序列:let seq1 = Observable.just(1) let seq2 = Observable.just(2) let seq: Observable<String> = Observable.zip(seq1, seq2) { (num1, num2) -> String in return "\(num1 + num2)" } seq.subscribe { (event) in print(event) } ---- example output ---- next(3) completed 複製程式碼
zip
方法按照引數個數的不同有多個版本,最多支援合併8個可觀察序列,需要注意的一點是,閉包所接收的引數是各個序列中對應位置的元素。也就是說,如果seq1傳送了一個事件,而seq2傳送了多個事件,閉包也只會被執行一次,seq中只有一個元素。組合的Observable中任意一個發生錯誤,最後的seq都會直接出錯終止,當所有的Observable都發出completed事件後,seq才會正常結束。
-
combineLatest:
combineLatest
同樣用於將多個序列組合成一個,使用方法與zip
一樣,但是它的呼叫機制跟zip
不同,每當其中一個序列有新元素時,combineLatest
都會從其他所有序列中取出最後一個元素,傳入閉包中生成新的元素新增到結果序列中。
Subject
Subject
物件相當於一種中間的代理和橋樑的作用,它既是觀察者又是可觀察序列,在向一個Subject
物件新增觀察者之後,可以通過該Subject
向其傳送事件。Subject
物件並不會主動傳送completed事件,並且在傳送了error或completed事件之後,Subject
中的序列會直接終結,無法再傳送新的訊息。Subject
同樣也分為幾種型別:
-
PublishSubject:
PublishSubject
的訂閱者只會收到在其訂閱(subscribe)之後傳送的事件let subject = PublishSubject<Int>() subject.onNext(1) subject.subscribe { (event) in print(event) } subject.onNext(2) ---- example output ---- next(2) 複製程式碼
可以看到,觀察者只收到了事件2,在訂閱之前傳送的事件1並沒有接收到。
-
ReplaySubject:
ReplaySubject
在初始化時指定一個大小為n的緩衝區,裡面會儲存最近傳送的n條事件,在訂閱之後,觀察者會立即收到緩衝區中的事件:let subject = ReplaySubject<Int>.create(bufferSize: 2) subject.onNext(1) subject.subscribe { (event) in print(event) } subject.onNext(2) ---- example output ---- next(1) next(2) 複製程式碼
-
BehaviorSubject:
BehaviorSubject
在初始化時需要提供一個預設值,在訂閱時觀察者會立刻收到序列上一次傳送的事件,如果沒有傳送過事件則會收到預設值:let subject = BehaviorSubject(value: 1) subject.subscribe { (event) in print(event) } ---- example output ---- next(1) 複製程式碼
-
Variable:
Variable
是對BehaviorSubject
的一個封裝,行為上與BehaviorSubject
類似。Variable
沒有on
之類的方法來傳送事件,取而代之的是一個value
屬性,向value
賦值可以向觀察者傳送next事件,並且訪問value
可以獲取最後一次傳送的資料:let variable = Variable(1) variable.asObservable().subscribe { (event) in print(event) } variable.value = 2 ---- example output ---- next(1) next(2) completed 複製程式碼
與其他Subject型別不同的是,
Variable
在釋放的時候會傳送completed事件,並且Variable
物件永遠不會傳送error事件。
Scheduler
Scheduler
是RxSwift中進行多執行緒程式設計的一種方式,一個Observable在執行的時候會指定一個Scheduler
,這個Scheduler
決定了在哪個執行緒對序列進行操作以及事件回撥。預設情況下,在訂閱Observable之後,觀察者會在與呼叫subscribe
方法時相同的執行緒收到通知,並且也會在該執行緒進行銷燬(dispose)。
與GCD類似,Scheduler
分為序列(serial)和並行(concurrent)兩種型別,RxSwift中定義了幾種Schedular:
- CurrentThreadScheduler:這是預設的Scheduler,代表了當前的執行緒,serial型別。
- MainScheduler:表示主執行緒,serial型別
- SerialDispatchQueueScheduler:提供了一些快捷的方法來建立序列Scheduler,內部封裝了DispatchQueue
- ConcurrentDispatchQueueScheduler:提供了快捷的方法來建立並行Scheduler,同樣封裝了DispatchQueue
subscribeOn和observeOn
subscribeOn
和observeOn
是其中兩個最重要的方法,它們可以改變Observable所在的Scheduler:
// main thread
let scheduler = ConcurrentDispatchQueueScheduler(qos: .default)
let seq = Observable.of(1, 2)
seq.subscribeOn(scheduler)
.map {
return $0 * 2 // 子執行緒
}
.subscribe { (event) in
print(event) // 子執行緒
}
複製程式碼
在上面的程式碼中建立了一個併發的Scheduler,並在序列seq上呼叫subscribeOn
指定了該Scheduler,可以看到,我們在主執行緒中訂閱該序列,但是map
方法以及事件的回撥都是在建立的子執行緒中執行。
subscribeOn
和observeOn
都可以指定序列的Scheduler,它們之間的區別在於:
subscribeOn
設定了整個序列開始的時候所在的Scheduler,序列在建立以及之後的操作都會在這個Scheduler上進行,subscribeOn
在整個鏈式呼叫中只能呼叫一次,之後再次呼叫subscribeOn
沒有任何效果。observeOn
指定一個Scheduler,在這之後的操作都會被派發到這個Scheduler上執行,observeOn
可以在鏈式操作的中間改變Scheduler
createObservable().
.doSomething()
.subscribeOn(scheduler1) // (1)
.doSomethingElse()
.observeOn(scheduler2) // (2)
.doAnother()
...
複製程式碼
如上程式碼,在(1)處執行了subscribeOn
之後,之前的操作createObservable()和doSomething()都會在scheduler1中執行,隨後的doSomethingElse()同樣也在scheduler1中執行,隨後用observeOn
指定了另外一個scheduler2,之後的doAnother()會在scheduler2上執行。
為原有程式碼新增Rx擴充套件
RxSwift中提供了一種擴充套件機制,可以很方便的為原有的程式碼新增上Rx擴充套件。首先來看一個結構體Reactive
:
public struct Reactive<Base> {
/// base是擴充套件的物件例項
public let base: Base
public init(_ base: Base) {
self.base = base
}
}
複製程式碼
Reactive
是一個泛型結構體,只定義了一個屬性base
,並且在初始化結構體的時候傳入該屬性的值。
此外還定義了一個協議ReactiveCompatible
:
public protocol ReactiveCompatible {
associatedtype CompatibleType
static var rx: Reactive<CompatibleType>.Type { get set }
var rx: Reactive<CompatibleType> { get set }
}
複製程式碼
該協議中分別為類物件和例項物件定義一個名字相同的屬性:rx
,型別為上面定義的Reactive
,隨後通過協議擴充套件為其提供了get
的預設的實現:
extension ReactiveCompatible {
public static var rx: Reactive<Self>.Type {
get {
return Reactive<Self>.self
}
set {
// this enables using Reactive to "mutate" base type
}
}
public var rx: Reactive<Self> {
get {
return Reactive(self)
}
set {
// this enables using Reactive to "mutate" base object
}
}
}
複製程式碼
關聯型別CompatibleType
被自動推導為實現該協議的類本身,使用self
初始化一個Reactive
物件。
最後通過協議擴充套件為所有的NSObject型別實現了ReactiveCompatible
協議:
extension NSObject: ReactiveCompatible { }
複製程式碼
這樣一來,程式碼中所有繼承自NSObject的型別例項中都會有一個型別為Reactive
的屬性rx
,當我們要為自己的型別新增Rx擴充套件時,只需要通過擴充套件向Reactive
中新增方法就可以了,例如向UIButton型別新增擴充套件:
extension Reactive where Base: UIButton { // 為Reactive<UIButton>新增擴充套件
public var tap: ControlEvent<Void> {
return controlEvent(.touchUpInside) // 通過base可以訪問該例項本身
}
}
複製程式碼
由於Reactive
是一個泛型型別,我們可以通過where語句指定泛型的型別,這樣一來,我們就可以在UIButton例項的rx中訪問tap屬性了:
let button = UIButton(...)
button.rx.tap
複製程式碼
類似RxCocoa這樣的RxSwift擴充套件庫都是通過這種方式進行Rx擴充套件的。