目錄
一、RxSwift是什麼,爲什麼要使用RxSwift
二、RxSwift的核心角色
1、Event
2、Observable
3、Observer要做的處理
4、Observer
三、Observer監聽Observable,以及監聽行爲的釋放
1、Observer監聽Observable
2、監聽行爲的釋放
四、Subjects
一、RxSwift是什麼,爲什麼要使用RxSwift
RxSwift是響應式編程的一個開源框架,所謂響應式編程(Reactive Programming,簡稱RP)就是一種編程範式——即怎麼寫代碼的方法論。
1️⃣如果項目裏你用的是MVVM架構的話,響應式編程則提供了更加簡單優雅的方式來做ViewModel和View的雙向綁定,從而使得你的MVVM更加如虎添翼;2️⃣傳統方式中我們經常使用代理、block、通知等方式來進行事件傳遞,它們各有各的寫法,而響應式編程則可以讓事件傳遞的方式做到統一——鏈式調用,同時能降低代碼的耦合度提高代碼內聚度(事件裏通常會攜帶數據,所以可以直接把事件看成是數據、把事件傳遞看成是數據傳遞),當然響應式編程還有很多其它方面的用途你可以去挖掘,比如在網絡請求方面的應用、在手勢方面的應用、在多個異步任務處理方面的應用等。
比較出名和成熟的響應式編程框架有:
- ReactiveCocoa,簡稱RAC,有OC和Swift版本
- ReactiveX,簡稱Rx,有衆多編程語言的版本,如RxJava、RxKotlin、RxSwift、RxDart等,它們的API基本都是一樣的,只不過是基於不同語言實現的而已,但是沒有OC版本,不過如果真得要用OC開發,我們也可以OC和Swift混編來調用RxSwift
Rx有這麼多編程語言的版本,足以說明大家都比較認可它的那一套機制,社區肯定要比RAC活躍得多,因此如果要選擇一個響應式編程框架,推薦Rx。
項目裏要想使用RxSwift,得先安裝RxSwift和RxCocoa這兩個框架,這裏只演示使用CocoaPods的安裝方式,像使用其它三方庫一樣直接在Podfile文件裏依賴這兩個框架,執行pod install即可。
pod 'RxSwift', '~> 5.0'
pod 'RxCocoa', '~> 5.0'
然後在想使用RxSwift的地方導入這兩個框架。
// RxSwift是Rx標準API的Swift版本實現,不包含任何iOS相關的內容
import RxSwift
// RxCocoa則是基於RxSwift給iOS的UI控件擴展了很多Rx特性
import RxCocoa
二、RxSwift的核心角色
iOS開發中的KVO、通知都是觀察者設計模式,在設計模式中它可是一種重中之重的設計模式。比如寶寶在睡覺,爸爸媽媽總不能一直在旁邊看着吧,那樣就太累了,他們該做啥事做啥事,只要聽到寶寶的哭聲,給寶寶餵奶就行了,這就是一個典型的觀察者設計模式。在這個例子裏,寶寶是被觀察者,爸爸媽媽是觀察者,只要被觀察者發出了事件——哭聲,觀察者收到後就會做出相應的處理——餵奶。據此我們可以得到觀察者設計模式的核心角色:
- 被觀察者(也叫發佈者)
- 被觀察者要發出的事件
- 觀察者(也叫訂閱者)
- 觀察者要做的處理
RxSwift跟觀察者設計模式很像,核心角色也是上面這四個。
1、Event
Event就是這麼一個泛型枚舉:
public enum Event<Element> {
case next(Element)
case error(Swift.Error)
case completed
}
這個泛型枚舉一共有三個值,所以事件一共有三種類型,也就是說Observable只可能發出這三種類型的事件:
- next事件:一個攜帶正常數據的正常事件,正常數據的數據類型就是這個泛型Element、它可以是任意類型、不過早在Observable那兒就指定好了,正常數據會被掛在事件的element屬性上
- error事件:一個攜帶錯誤信息的錯誤事件,錯誤信息的數據類型爲Error類型,錯誤信息會被掛在事件的error屬性上
// Error其實是一個協議,要想發出error事件,我們就得自定義Error
enum CustomError: Error {
case error1
case error2
case error3
}
- completed事件:一個什麼數據都不攜帶的完成事件
Event就是事件,它就是Observable隨着時間的推移源源不斷髮出的東西。事件的作用就是作爲數據的載體來攜帶數據,next事件就是正常數據的載體,error事件就是錯誤信息的載體,也就是說Observable要想發送一個正常數據就發出一個next事件,Observable要想發送一個錯誤信息就發出一個error事件,需要注意的是Observable一旦發出error事件就代表它因錯誤而終止了,以後再也不會發出事件。completed事件雖然不是數據的載體,但它也是一個不可或缺的事件,Observable一旦發出completed事件就代表它正常終止了,以後再也不會發出事件。
2、Observable
Observable就是這麼一個泛型類:
// 注意:類型爲ObservableType
public class Observable<Element> : ObservableType {
...
}
Observable就是被觀察者,它其實是一個事件序列(Event Sequence),所以我們通常又稱它爲被觀察序列。Observable的作用就是隨着時間的推移源源不斷地發出事件,你想它發出的事件掛什麼數據類型的數據就把它的泛型Element搞成什麼數據類型就行了。
- 創建Observable及Observable發出事件
// 1、怎麼創建一個observable?
//
// 調用Observable的類方法create就可以創建一個observable,假設這個observable發出的事件掛的數據爲Int類型
//
// create方法的入參是一個閉包,這個閉包的入參是一個observer——這個observer就是將來監聽該observable的observer,這個閉包的返回值是一個disposable
// create方法的返回值是一個observable
let observable = Observable<Int>.create { observer in
// 2、observable怎麼發出事件?
//
// 這個閉包屬於observable,所以在這個環境裏:
// observable給observer發出next事件,換句話說就是給observer發送一個next消息(消息機制),即讓observer調用一下它的onNext方法
// observable給observer發出completed事件或error事件,同理
observer.onNext(1)
observer.onNext(2)
observer.onNext(3)
observer.onCompleted()
// observer.onError(CustomError.error1)
// 因爲observable一旦發出completed事件或error事件就終止了,所以後面這三個事件是發不出去的
observer.onNext(4)
observer.onNext(5)
observer.onNext(6)
return Disposables.create()
}
- 快捷創建Observable及Observable發出事件
// 單個事件Observable:該Observable只能發出一個next事件 + 一個completed事件
//
// 1、調用Observable的類方法just就可以快捷創建一個單個事件observable,可以省略泛型,直接傳你想發送的數據就行,會自動給你泛好
// 2、創建好、被監聽後,該Observable就會自動發出事件
let justObservable = Observable.just(1)
// 等價於
let observable = Observable<Int>.create { observer in
observer.onNext(1)
observer.onCompleted()
return Disposables.create()
}
// 多個事件Observable:該Observable能發出多個next事件 + 一個completed事件
//
// 1、調用Observable的類方法from或of就可以快捷創建一個多個事件observable,可以省略泛型,直接傳你想發送的數據就行,會自動給你泛好
// 2、創建好、被監聽後,該Observable就會自動發出事件
let fromObservable = Observable.from([1, 2, 3]) // 通過數組來生成多個next事件
let ofObservable = Observable.of(1, 2, 3) // 通過可變參數來生成多個next事件
// 等價於
let observable = Observable<Int>.create { observer in
observer.onNext(1)
observer.onNext(2)
observer.onNext(3)
observer.onCompleted()
return Disposables.create()
}
// 重複N次Observable:給定一個重複次數N,該Observable就會重複發出同一個next事件N次 + 一個completed事件
let repeatElementObservable = Observable.repeatElement(1)
repeatElementObservable.take(5)
// 定時器Observable:完全就是一個定時器,必須泛整型。延遲多長時間、每隔多長時間、在哪個線程,該Observable會像個定時器一樣不斷髮出累加的整數
//
// 如果想取消定時器,其實不用取消定時器Observable,你只需要把監聽行爲給釋放掉就可以了,這樣的話定時器Observable還是存在的,它並不會發出completed事件或error事件
let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
3、Observer要做的處理
這個沒什麼好說的,通常就是一個閉包。
4、Observer
Observer有兩種類型:AnyObserver(隨便什麼東西都能充當AnyObserver這種類型的觀察者)和Binder(一般都是一些UI控件的屬性在充當Binder這種類型的觀察者),但它們都是一個泛型結構體:
// 注意:類型爲ObserverType
public struct AnyObserver<Element> : ObserverType {
...
}
// 注意:類型爲ObserverType
public struct Binder<Element>: ObserverType {
...
}
Observer就是觀察者。Observer的作用就是監聽Observable(至於怎麼監聽,會在下一小節說),並在收到Observable發出的事件時做出相應的處理,需要注意的是Observer想要監聽的數據的數據類型就是泛型的這個Element,如果它跟Observable和Event那裏的泛型Element不一樣,我們就得先通過map等函數把數據類型給搞一樣。
- 創建一個AnyObserver及AnyObserver做相應的處理
// 1、怎麼創建一個anyObeserver?
//
// 調用AnyObserver的init方法就可以創建一個anyObserver,假設這個anyObserver想要監聽的數據的數據類型爲Int
//
// init方法的入參是一個閉包,這個閉包的入參是一個event——這個event就是將來被觀察的Observable所發出的事件,這個閉包的執行體就是AnyObserver要做的處理
let anyObserver = AnyObserver<Int>.init { event in
// 2、anyObserver怎麼做出相應的處理?
// 在這個閉包的執行體裏做相應的處理即可
switch event {
case .next:
print("next:\(event.element)")
case .error:
print("error:\(event.error)")
case .completed:
print("completed")
}
}
- 創建一個Binder及Binder做相應的處理
// 1、怎麼創建一個Binder?
//
// 調用Binder的init方法就可以創建一個binder,假設這個binder想要監聽的數據的數據類型爲String(假設我們想監聽字符串數據,把監聽到的字符串數據設置到一個label上顯示)
//
// init方法的第一個入參是指你要把監聽到的數據設置給誰來使用,通常是一個UI控件
// init方法的第二個入參是一個閉包,這個閉包的第一個入參就是前面的那個UI控件,第二個入參就是監聽到的字符串數據,這個閉包的執行體就是binder要做的處理
let binder = Binder<String>.init(label) { label, element in
// 2、binder怎麼做出相應的處理?
// 在這個閉包的執行體裏做相應的處理即可
label.text = element
}
三、Observer監聽Observable,以及監聽行爲的釋放
1、Observer監聽Observable
現在我們已經成功擁有了四大核心角色,被觀察者 + 被觀察者要發出的事件、觀察者 + 觀察者要做的處理,那這兩大陣營怎麼聯繫起來呢,也就是說觀察者怎麼才能收到被觀察者發出的事件、進而觸發相應的處理呢?很簡單,就是Observer監聽Observable。
- AnyObserver監聽Observable
let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
let anyObserver = AnyObserver<Int>.init { event in
switch event {
case .next:
print("next:\(event.element)") // 會打印:0、1、2、3、...
case .error:
print("error:\(event.error)")
case .completed:
print("completed")
}
}
// anyObserver監聽timerObservable,就這麼簡單
timerObservable.subscribe(anyObserver)
- Binder監聽Observable
// 界面上搞一個label
@IBOutlet weak var label: UILabel!
let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
let binder = Binder<String>.init(label) { label, element in
label.text = element
}
// binder監聽timerObservable
// 但是timerObservable發出的事件掛的數據是Int類型
// 而binder監聽的確實String類型
// 所以得把timerObservable發出的事件掛的Int數據通過map函數轉換成String數據
// 就這麼簡單
timerObservable
.map { element in
return "數字是:\(element)"
}
.subscribe(binder)
// Binder還有一個專門的bindTo方法用來監聽Observable,不過跟subscribe方法是等價的
timerObservable
.map { element in
return "數字是:\(element)"
}
.bind(to: binder)
- 當然除了AnyObserver和Binder這種標標準準的Observer可以監聽Observable之外,我們還可以直接用一個閉包來監聽Observable,這種情況下這個閉包其實也是一個廣義上的觀察者,而且這個觀察者是把原來標標準準的觀察者和觀察者要做的處理兩個角色融爲一體了
let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
// 方式1
timerObservable
.subscribe { event in
switch event {
case .next:
print("next:\(event.element)") // 會打印:0、1、2、3、...
case .error:
print("error:\(event.error)")
case .completed:
print("completed")
}
}
// 方式2
timerObservable
.subscribe(onNext: { element in
print("next:\(element)") // 會打印:0、1、2、3、...
}, onError: { error in
print("error:\(error)")
}, onCompleted: {
print("completed")
})
因此將來我們看到subscribe方法或bindTo方法前面的東西肯定就是個Observable,後面的東西肯定就是個廣義的Observer——標標準準的Observer + 閉包。
2、監聽行爲的釋放
其實每當Observer監聽Observable時就產生了一個監聽行爲——即subscribe方法或bindTo方法會返回一個Disposable類型的實例,一定要記得在適當的時機釋放這個監聽行爲,以免內存泄漏,注意當這個監聽行爲被釋放後,僅僅是切斷了兩大陣營之間的聯繫,Observable和Observer這些對象本身是沒被銷燬的。釋放的方式有三種:
- 立即釋放監聽行爲(實際開發中使用較少)
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
let anyObserver = AnyObserver<Int>.init { event in
switch event {
case .next:
print("next:\(event.element)")
case .error:
print("error:\(event.error)")
case .completed:
print("completed")
}
}
let disposable = timerObservable.subscribe(anyObserver)
// 立即釋放監聽行爲——即接收完釋放之前的一波數據後立即釋放,類似於release
disposable.dispose()
}
}
- 自動釋放監聽行爲(實際開發中使用較多)
class ViewController: UIViewController {
// 類似於autoreleasepool
//
// 它會把所有的監聽行爲都放進去,等到它即將銷燬時再對它裏面所有的監聽行爲都調用一次dispose方法
// 現在bag屬性的生命週期是跟當前viewController一樣的
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
let anyObserver = AnyObserver<Int>.init { event in
switch event {
case .next:
print("next:\(event.element)")
case .error:
print("error:\(event.error)")
case .completed:
print("completed")
}
}
timerObservable.subscribe(anyObserver).disposed(by: bag)
}
}
- 手動釋放監聽行爲
class ViewController: UIViewController {
// 定義一個屬性
var disposable: Disposable?
override func viewDidLoad() {
super.viewDidLoad()
let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
let anyObserver = AnyObserver<Int>.init { event in
switch event {
case .next:
print("next:\(event.element)")
case .error:
print("error:\(event.error)")
case .completed:
print("completed")
}
}
// 把監聽行爲記錄下來
disposable = timerObservable.subscribe(anyObserver)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// 在合適的時機手動釋放監聽行爲
disposable?.dispose()
}
}
四、Subjects
Subjects同時充當了Observable和Observer的角色,也就是說它既是一個被觀察者、也是一個觀察者,它既能發出事件、也能做相應的處理。常用的Subjects有四種:
- PublishSubject
class ViewController: UIViewController {
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let publishSubject = PublishSubject<Int>.init()
// 這裏publishSubject在充當Observer的角色,幫Observable發出事件
// 還記得之前創建Observable時,Observable是怎麼發出事件的嗎?就是給Observer發送onNext消息發出的,onNext本來就是Observer的方法,所以這裏就體現了publishSubject在充當Observer的角色
publishSubject.onNext(1)
publishSubject.onNext(2)
publishSubject.onNext(3)
// 這裏publishSubject在充當Observable的角色,只會向觀察者發送觀察者監聽它之後的事件,不會向觀察者發送觀察者監聽它之前的事件
publishSubject.subscribe { event in
print(event) // 4、5、6
}.disposed(by: bag)
publishSubject.onNext(4)
publishSubject.onNext(5)
publishSubject.onNext(6)
}
}
- BehaviorSubject
class ViewController: UIViewController {
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let behaviorSubject = BehaviorSubject<Int>(value: 1)
// 這裏behaviorSubject在充當Observer的角色,幫Observable發出事件
behaviorSubject.onNext(2)
behaviorSubject.onNext(3)
// 這裏behaviorSubject在充當Observable的角色,會向觀察者發送觀察者監聽它之前的最後一個事件 + 觀察者監聽它之後的事件
behaviorSubject.subscribe { event in
print(event) // 3、4、5、6
}.disposed(by: bag)
behaviorSubject.onNext(4)
behaviorSubject.onNext(5)
behaviorSubject.onNext(6)
}
}
- ReplaySubject
class ViewController: UIViewController {
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let replaySubject = ReplaySubject<Int>.create(bufferSize: 2) // 假設要緩存兩個事件
// 這裏replaySubject在充當Observer的角色,幫Observable發出事件
replaySubject.onNext(1)
replaySubject.onNext(2)
replaySubject.onNext(3)
// 這裏replaySubject在充當Observable的角色,會向觀察者發送觀察者監聽它之前緩存的N個事件 + 觀察者監聽它之後的事件
replaySubject.subscribe { event in
print(event) // 2、3、4、5、6。緩存的是最後兩個事件,因爲是序列,所以會按順序把事件發出來
}.disposed(by: bag)
replaySubject.onNext(4)
replaySubject.onNext(5)
replaySubject.onNext(6)
}
}
- BehaviorRelay(Variable已經被廢棄了,用BehaviorRelay代替,它倆其實都是對BehaviorSubject的封裝,功能一樣)
class ViewController: UIViewController {
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let behaviorRelay = BehaviorRelay<Int>.init(value: 1)
// 這裏behaviorRelay在充當Observer的角色,幫Observable發出事件
behaviorRelay.accept(2)
behaviorRelay.accept(3)
// 這裏behaviorRelay在充當Observable的角色,會向觀察者發送觀察者監聽它之前的最後一個事件 + 觀察者監聽它之後的事件
behaviorRelay.subscribe { event in
print(event) // 3、4、5、6
}.disposed(by: bag)
behaviorRelay.accept(4)
behaviorRelay.accept(5)
behaviorRelay.accept(6)
}
}
參考
1、RxSwift中文文檔
2、RxSwift大全