簡介
它拓展了觀察者模式。使你能夠自由組合多個(gè)異步事件,而不需要去關(guān)心線程,同步,線程安全,并發(fā)數(shù)據(jù)以及I/O阻塞。
函數(shù)響應(yīng)式編程
- 函數(shù)式編程:是種編程范式,它需要我們將函數(shù)作為參數(shù)傳遞,或者作為返回值返還。我們可以通過組合不同的函數(shù)來得到想要的結(jié)果。
- 響應(yīng)式編程: a=b+c表示將表達(dá)式的結(jié)果賦給 a,而之后改變 b 或 c的值不會影響 a。但在響應(yīng)式編程中,a的值會隨著 b或 c的更新而更新。a=b+c聲明的是一種綁定關(guān)系。
RxSwift優(yōu)點(diǎn)
- 在業(yè)務(wù)層面實(shí)現(xiàn)代碼邏輯分離,方便后期維護(hù)和拓展
- 極大提高程序響應(yīng)速度,充分發(fā)掘CPU的能力
- 幫助開發(fā)者提高代碼的抽象能力和充分理解業(yè)務(wù)邏輯
- Rx豐富的操作符會幫助我們極大的簡化代碼邏輯
核心
- Observable - 可被監(jiān)聽的序列產(chǎn)列
- Observer - 觀察者
- Operator - 操作符
- Disposable - 管理綁定(訂閱)的生命周期
- Schedulers - 調(diào)度器(線程隊(duì)列調(diào)配)
Observable - 可被監(jiān)聽的序列
框架已經(jīng)幫我們創(chuàng)建好了許多常用的序列。例如:button的點(diǎn)擊,textField的當(dāng)前文本,switch的開關(guān)狀態(tài),slider的當(dāng)前數(shù)值等等。
let numbers: Observable<Int> = Observable.create { observer -> Disposable in
observer.onNext(0)
observer.onNext(1)
observer.onNext(2)
observer.onNext(3)
observer.onNext(4)
observer.onNext(5)
observer.onNext(6)
observer.onNext(7)
observer.onNext(8)
observer.onNext(9)
observer.onCompleted()
return Disposables.create()
}
Observer - 觀察者
觀察者最直接的方法就是在 Observable 的 subscribe 方法后面描述,事件發(fā)生時(shí),需要如何做出響應(yīng)。而觀察者就是由后面的 onNext,onError,onCompleted的這些閉包構(gòu)建出來的。
- observer.onNext(0) 就代表產(chǎn)生了一個(gè)元素,他的值是 0。
- onCompleted()結(jié)束
- onError()發(fā)生錯誤
tap.subscribe(onNext: { [weak self] in
self?.showAlert()
}, onError: { error in
print("發(fā)生錯誤: \(error.localizedDescription)")
}, onCompleted: {
print("任務(wù)完成")
})
操作符
- filter - 過濾
// 溫度
let rxTemperature: Observable<Double> = ...
// filter 操作符
rxTemperature.filter { temperature in temperature > 33 }
.subscribe(onNext: { temperature in
print("高溫:\(temperature)度")
})
.disposed(by: disposeBag)
- map - 轉(zhuǎn)換
let disposeBag = DisposeBag()
Observable.of(1, 2, 3)
.map { $0 * 10 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
- zip - 配對
let disposeBag = DisposeBag()
let first = PublishSubject<String>()
let second = PublishSubject<String>()
Observable.zip(first, second) { $0 + $1 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
first.onNext("1")
second.onNext("A")
1A
Disposable - 可被清除的資源
通常來說,一個(gè)序列如果發(fā)出了 error 或者 completed 事件,那么所有內(nèi)部資源都會被釋放。如果你需要提前釋放這些資源或取消訂閱的話,那么你可以對返回的 可被清除的資源(Disposable) 調(diào)用 dispose 方發(fā)
通常情況下,我們不需要手動調(diào)用 dispose 方法的
使用 清除包(DisposeBag) 或者 takeUntil 操作符 來管理訂閱的生命周期
- DisposeBag - 清除包
var disposeBag = DisposeBag()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
textField.rx.text.orEmpty
.subscribe(onNext: { text in print(text) })
.disposed(by: self.disposeBag)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.disposeBag = DisposeBag()
}
- takeUntil
使得訂閱一直持續(xù)到控制器的 dealloc 事件產(chǎn)生為止
override func viewDidLoad() {
super.viewDidLoad()
...
_ = usernameValid
.takeUntil(self.rx.deallocated)
.bind(to: passwordOutlet.rx.isEnabled)
_ = usernameValid
.takeUntil(self.rx.deallocated)
.bind(to: usernameValidOutlet.rx.isHidden)
_ = passwordValid
.takeUntil(self.rx.deallocated)
.bind(to: passwordValidOutlet.rx.isHidden)
_ = everythingValid
.takeUntil(self.rx.deallocated)
.bind(to: doSomethingOutlet.rx.isEnabled)
_ = doSomethingOutlet.rx.tap
.takeUntil(self.rx.deallocated)
.subscribe(onNext: { [weak self] in self?.showAlert() })
}
Schedulers - 調(diào)度器
Schedulers 是 Rx 實(shí)現(xiàn)多線程的核心模塊,它主要用于控制任務(wù)在哪個(gè)線程或隊(duì)列運(yùn)行。
GCD:
// 后臺取得數(shù)據(jù),主線程處理結(jié)果
DispatchQueue.global(qos: .userInitiated).async {
let data = try? Data(contentsOf: url)
DispatchQueue.main.async {
self.data = data
}
}
RxSwift:
let rxData: Observable<Data> = ...
rxData
.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] data in
self?.data = data
})
.disposed(by: disposeBag)
subscribeOn 來決定數(shù)據(jù)序列的構(gòu)建函數(shù)在哪個(gè) Scheduler 上運(yùn)行。以上例子中,由于獲取 Data 需要花很長的時(shí)間,所以用 subscribeOn 切換到 后臺 Scheduler 來獲取 Data。這樣可以避免主線程被阻塞。
observeOn 來決定在哪個(gè) Scheduler 監(jiān)聽這個(gè)數(shù)據(jù)序列。以上例子中,通過使用 observeOn 方法切換到主線程來監(jiān)聽并且處理結(jié)果。
MainScheduler 主線程
SerialDispatchQueueScheduler
抽象了串行 DispatchQueue。如果你需要執(zhí)行一些串行任務(wù),可以切換到這個(gè) Scheduler 運(yùn)行。ConcurrentDispatchQueueScheduler
抽象了并行 DispatchQueue。如果你需要執(zhí)行一些并發(fā)任務(wù),可以切換到這個(gè) Scheduler 運(yùn)行。OperationQueueScheduler 抽象了 NSOperationQueue。它具備 NSOperationQueue 的一些特點(diǎn),例如,你可以通過設(shè)置 maxConcurrentOperationCount,來控制同時(shí)執(zhí)行并發(fā)任務(wù)的最大數(shù)量。
Error Handling - 錯誤處理
RxSwift 主要有兩種錯誤處理機(jī)制:
- retry - 重試
- catch - 恢復(fù)
retry - 重試
- retry
let rxJson: Observable<JSON> = ...
rxJson
.retry(3)
.subscribe(onNext: { json in
print("取得 JSON 成功: \(json)")
}, onError: { error in
print("取得 JSON 失敗: \(error)")
})
.disposed(by: disposeBag)
失敗后重試三次
- retryWhen
操作符,這個(gè)操作符主要描述應(yīng)該在何時(shí)重試,并且通過閉包里面返回的 Observable 來控制重試的時(shí)機(jī):
let maxRetryCount = 4 // 最多重試 4 次
let retryDelay: Double = 5 // 重試延時(shí) 5 秒
rxJson
.retryWhen { (rxError: Observable<Error>) -> Observable<Int> in
return rxError.flatMapWithIndex { (error, index) -> Observable<Int> in
guard index < maxRetryCount else {
return Observable.error(error)
}
return Observable<Int>.timer(retryDelay, scheduler: MainScheduler.instance)
}
}
.subscribe(...)
.disposed(by: disposeBag)
catchError - 恢復(fù)
- catchError
可以在錯誤產(chǎn)生時(shí),用一個(gè)備用元素或者一組備用元素將錯誤替換掉:
let rxData: Observable<Data> = ... // 網(wǎng)絡(luò)請求的數(shù)據(jù)
let cahcedData: Observable<Data> = ... // 之前本地緩存的數(shù)據(jù)
rxData
.catchError { _ in cahcedData }
.subscribe(onNext: { date in
print("獲取數(shù)據(jù)成功: \(date.count)")
})
.disposed(by: disposeBag)
- catchErrorJustReturn 當(dāng)錯誤產(chǎn)生時(shí),就返回一個(gè)空數(shù)組,于是就會顯示一個(gè)空列表頁。
Result
展示錯誤結(jié)果
updateUserInfoButton.rx.tap
.withLatestFrom(rxUserInfo)
.flatMapLatest { userInfo -> Observable<Result<Void>> in
return update(userInfo)
.map(Result.success) // 轉(zhuǎn)換成 Result
.catchError { error in Observable.just(Result.failure(error)) }
}
.observeOn(MainScheduler.instance)
.subscribe(onNext: { result in
switch result { // 處理 Result
case .success:
print("用戶信息更新成功")
case .failure(let error):
print("用戶信息更新失?。?\(error.localizedDescription)")
}
})
.disposed(by: disposeBag)
其他
后面會講下具體實(shí)踐-UI上的使用,還有更多理論性的東西就看文檔就ok了,操作符遇到了再查。
基本常用控件
UITablView和UICollectionview使用
其他不常用控件
所有例子demo