為什么要用Rx
傳統(tǒng)的編程方式大多都是告訴程序需要做什么漩符、怎么做思恐、什么時候做荒吏,并通過KVO敛惊、Notification、Delegate監(jiān)聽變化绰更,同時又需要告訴系統(tǒng)什么時候會發(fā)生變化豆混。ReactiveX可以幫助我們讓代碼自動相應更新,程序可以對底層數(shù)據(jù)的變化做出響應动知。
RxSwift是ReactiveX的Swift版,RxCocoa使用RxSwift對Cocoa APIs響應式編程的封裝员辩。
比如讓一個Button響應單擊事件盒粮,非響應式編程是這樣:
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
func buttonAction() {
...
}
用了RxCocoa后變成這樣:
button.rx.tap.subscribe(onNext: {
...
})
代碼變得更加簡潔,不需要定義多余的函數(shù)奠滑,從而可以讓開發(fā)者更專注于業(yè)務(wù)邏輯丹皱,不再維護中間狀態(tài)。
Rx的一些重點
Observables和Observers
兩個重要的概念:Observable和Observer
- Observable是發(fā)生變化的對象
- Observer是接收變化通知的對象
多個Observer可以監(jiān)聽同一個Observable宋税,Observable發(fā)生變化時會通知所有訂閱的Observer摊崭。
Observable也相當于事件管道,會向訂閱者發(fā)送事件信息杰赛。事件分為三種:
- .Next(value) 有新的數(shù)據(jù)呢簸。
- .Completed 管道已經(jīng)結(jié)束。
- .Error 有異常發(fā)生導致事件管道結(jié)束乏屯。
例如一個網(wǎng)絡(luò)請求根时,收到服務(wù)端返回的數(shù)據(jù)后會發(fā)送.Next,因為請求都是一次性的辰晕,所以Next發(fā)送完后會馬上發(fā)送Completed蛤迎。如果請求超時了則會發(fā)送Error。
DisposeBag
DisposeBag是RxSwift提供的用來管理內(nèi)存的工具含友。
當帶有DisposeBag屬性的對象調(diào)用deinit()時替裆,bag將被清空校辩,且每一個Observer會自動取消訂閱它所觀察的對象。理念類似于autoreleasepool辆童。
如果沒有DisposeBag宜咒,則會產(chǎn)生retain cycle,或者被意外釋放掉胸遇,導致程序Crash荧呐。
let _disposeBag = DisposeBag()
Observable.just("123").subscribe(onNext: { (item) in
}).addDisposableTo(_disposeBag)
Disposables.create
每個Observable都要求Disposable類型的返回值,通過Disposables.create便能創(chuàng)建Disposable的實例纸镊,當Observable被釋放時會執(zhí)行Disposable倍阐,相當于析構(gòu)函數(shù)。
Observable.create
實際開發(fā)中用的最多的函數(shù)逗威,創(chuàng)建一個Observable峰搪。
Observable.create { (observer) -> Disposable in
...
if (error) {
observer.on(.error(RxSwiftError.unknown))
} else {
observer.on(.next(value))
observer.on(.completed)
}
return Disposables.create {
}
}
Observable.just
just只會發(fā)送一條數(shù)據(jù),它先發(fā)送一次.Next(value)凯旭,然后發(fā)送.Completed概耻。
Observable.empty
empty是空管道,它只會發(fā)送.Completed消息罐呼。
Observable.deferred
deferred會等到有Observer的時候再去創(chuàng)建Observable鞠柄,相當于懶加載,而且每個訂閱者訂閱的對象都是內(nèi)容相同但指針不同的Observable嫉柴。
Subjects
Subjet是observable和Observer之間的橋梁厌杜,它既是Obserable又是Observer,既可以發(fā)出事件计螺,也可以訂閱事件夯尽。
PublishSubject只能收到訂閱它之后的事件,訂閱之前的事件無法收到登馒。
Debug
打印所有訂閱匙握、事件、Disposable陈轿。
sequenceThatErrors
.retry(3)
.debug()
.subscribe(onNext: { print($0) })
.addDisposableTo(disposeBag)
實踐(封裝CoreBluetooth)
PTRxBluetooth主要分為三個類:
-
PTCBCentralManager
封裝系統(tǒng)原生類CBCentralManager
fileprivate let didUpdateStateSubject = PublishSubject<PTBluetoothState>() extension PTCBCentralManager: CBCentralManagerDelegate { public func centralManagerDidUpdateState(_ central: CBCentralManager) { if let bleState = PTBluetoothState(rawValue: central.state.rawValue) { didUpdateStateSubject.onNext(bleState) } } }
第一步:定義一個PublishSubject類型的屬性
第二步:實現(xiàn)協(xié)議CBCentralManagerDelegate圈纺,在centralManagerDidUpdateState回調(diào)函數(shù)中,調(diào)用onNext將藍牙狀態(tài)發(fā)送給訂閱者济欢。
每個回調(diào)函數(shù)都有一個與之相對應的PublishSubject屬性赠堵,在函數(shù)執(zhí)行時調(diào)用onNext將數(shù)據(jù)發(fā)送給訂閱者,上層只需要訂閱Subject就能得到數(shù)據(jù)狀態(tài)法褥,不再需要代理或callback茫叭。
-
PTBluetoothClient
RxBluetooth的主要入口,封裝了所有藍牙操作半等。上層在檢查藍牙狀態(tài)揍愁、搜尋設(shè)備呐萨、連接設(shè)備時主要與這個類交互。
//監(jiān)聽手機的藍牙開啟狀態(tài) public var state: Observable<PTBluetoothState> { return .deferred { return self.base.centralManager.rx.didUpdateState.startWith(self.base.centralManager.state) } }
第一步:調(diào)用了.deferred莽囤,deferred會等到有Observer訂閱它的時候去創(chuàng)建谬擦,并且給每個Observer都創(chuàng)建一個新對象。
第二步:調(diào)用centralMangaer的函數(shù)didUpdateState朽缎,并使用startWith賦初始值惨远,通常是unknown。
關(guān)于startWith可以參考官方文檔startWith
//搜尋周圍的藍牙設(shè)備 public func scanForPeripherals() -> Observable<PTCBPeripheral> { base.scanPeripheral.removeAll() return .deferred { let observable = Observable.create { (element: AnyObserver<PTCBPeripheral>) -> Disposable in let disposeable = self.base.centralManager.rx.didDiscoverPeripheral .map { return $0 } .subscribe(onNext: { (peripheral) in if self.base.scanPeripheral.contains(peripheral) == false { self.base.scanPeripheral.append(peripheral) element.onNext(peripheral) } }) self.base.centralManager.scanForPeripherals(withServices: nil, options: nil) return Disposables.create { disposeable.dispose() self.base.centralManager.stopScan() } } .subscribeOn(self.base.subscriptionQueue) .publish() .refCount() return self.base.ensureBluetoothState(.poweredOn, observable: observable) } }
第一步:創(chuàng)建一個Observable话肖。
第二步:訂閱centralManager的didDiscoverPeripheral北秽,當搜尋到新設(shè)備時,發(fā)送給observable的訂閱者最筒。
第三步:調(diào)用函數(shù)scanForPeripherals開始搜尋藍牙設(shè)備贺氓。
第四步:observable釋放時,取消訂閱didDiscoverPeripheral床蜘,并停止搜尋設(shè)備辙培。
第五步:調(diào)用publish和refcount,這樣做是為了將冷信號(Cold Observable)轉(zhuǎn)成熱信號(Hot Observable)邢锯。
1.Hot Observable是主動的扬蕊,既是沒有訂閱者,它依然會發(fā)送事件丹擎。Cold Observable是被動的厨相,只有出現(xiàn)訂閱者后,它才會發(fā)送事件鸥鹉。
2.Hot Observable可以有多個訂閱者,Cold Observable只能一對一庶骄,當出現(xiàn)多個訂閱者時毁渗,事件會從新發(fā)送。
//嘗試連接藍牙設(shè)備 public func connect(_ peripheral: PTCBPeripheral, options: [String: Any]? = nil) -> Observable<PTCBPeripheral> { let success = base.centralManager.rx.didConnectPeripheral .filter { $0 == peripheral } .take(1) .map { _ in return peripheral } let error = base.centralManager.rx.didFailConnectPeripheral .filter { $0.0 == peripheral } .take(1) .flatMap { (peripheral, error) -> Observable<PTCBPeripheral> in return .empty() } let observable = Observable<PTCBPeripheral>.create { (observer) -> Disposable in guard peripheral.isConnected == false else { observer.onNext(peripheral) observer.onCompleted() return Disposables.create() } let disposable = success.amb(error).subscribe(observer) self.base.centralManager.connect(peripheral, options: options) return Disposables.create { if peripheral.isConnected == false { self.base.centralManager.cancelPeripheralConnection(peripheral) disposable.dispose() } } } return observable }
第一步:訂閱連接成功和連接失敗的Observable单刁。
第二步:創(chuàng)建一個Observable灸异。
第三步:調(diào)用amb將success和error綁定在一起,amb的主要作用就是將多個Observable綁定羔飞,但是只有第一個Observable能夠發(fā)送onNext肺樟,其他的Observable只能發(fā)送onError和onCompleted。因為連接失敗是沒有onNext的逻淌,所以這里用到了amb么伯。
-
PTCBPeripheral
PTCBPeripheral封裝系統(tǒng)類CBPeripheral,主要用來與藍牙設(shè)備交互卡儒,比如接收藍牙數(shù)據(jù)田柔、發(fā)送數(shù)據(jù)給設(shè)備俐巴。
extension PTCBPeripheral: CBPeripheralDelegate { public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { let services = peripheral.services ?? [] services.forEach { peripheralDidDiscoverServicesSubject.onNext($0) } } public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { let characteristics = service.characteristics ?? [] peripheralDidDiscoverCharacteristicsSubject.onNext(characteristics) } public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { peripheralDidUpdateValueSubject.onNext(characteristic) } }
實現(xiàn)CBPeripheralDelegate代理,當搜尋到Services和Characteristics時硬爆,相對應的PublishSubject會發(fā)送數(shù)據(jù)給訂閱者欣舵。
上層需要搜尋設(shè)備的Service時,只需要這樣:
aPeripheral.rx.discoverServices(nil).subscribe(onNext: { (service) in }).addDisposableTo(_disposeBag)