Driver
能夠說是最複雜的 trait,它的目標是提供一種簡便的方式在 UI 層編寫響應式代碼。MainScheduler
)shareReplayLatestWhileConnected
)Driver
最常使用的場景應該就是須要用序列來驅動應用程序的狀況了,好比:經過 CoreData
模型驅動 UI,使用一個 UI 元素值(綁定)來驅動另外一個 UI 元素值。咱們來實現這樣一個需求:根據一個輸入框的關鍵字,來請求數據,而後將獲取到的結果同時綁定到label和button上。api
override func viewDidLoad() {
super.viewDidLoad()
let result = inputTF.rx.text.orEmpty
.flatMap {
return self.request(text: $0)
.observeOn(MainScheduler.instance) // 將返回結果切換到到主線程上
.catchErrorJustReturn("網絡請求失敗") // 錯誤被處理了,這樣至少不會終止整個序列
}.share(replay: 1, scope: .whileConnected) // //HTTP 請求是被共享的,當序列綁定屢次市防止重複發送同一個請求
result.map{ "\($0 as! String)" }
.bind(to: textLabel.rx.text)
.disposed(by: disposeBag)
result.map{ "\($0 as! String)" }
.bind(to: btn.rx.title())
.disposed(by: disposeBag)
}
// 模擬請求網絡
func request(text: String) -> Observable<Any> {
print("開始請求網絡\(Thread.current)")
return Observable<Any>.create({ (ob) -> Disposable in
if text == "110" {
ob.onError(NSError.init(domain: "網絡請求錯誤", code: 111, userInfo: nil))
}
DispatchQueue.global().async {
print("請求完成\(Thread.current)")
ob.onNext("請求結果:\(text)")
ob.onCompleted()
}
return Disposables.create()
})
}
複製代碼
如上面的代碼註釋所示:爲了解決一些可能存在的問題,咱們都須要去加額外的加一些處理,保證程序正常運行,這是很是不友好的,由於對於一個大型的項目來講,若是都這麼幹也太麻煩了,並且容易遺漏出錯。因此咱們須要用另一種方式來實現一樣的功能。安全
Driver
這個高階函數實現override func viewDidLoad() {
super.viewDidLoad()
let result = inputTF.rx.text.orEmpty
.asDriver() // 將序列轉換爲Driver序列
.flatMap {
return self.request(text: $0)
.asDriver(onErrorJustReturn: "檢測到了錯誤事件")
}
// 將結果綁定到textLabel顯示,注意這裏使用的是drive而不是bindTo
let _ = result.map { "\($0 as! String)" } // 映射
.drive(self.textLabel.rx.text)
.disposed(by: disposeBag)
// 將結果綁定到btn顯示
let _ = result.map { "\($0 as! String)" }
.drive(self.btn.rx.title())
.disposed(by: disposeBag)
}
複製代碼
相比第一張實現方式,使用Driver
實現簡單不少,那麼,咱們一塊兒來看看Driver
是如何幫咱們實現這些問題的。 首先先對源序列調用了asDriver
方法進行轉換,進入到asDriver
方法裏面查看源碼bash
public func asDriver() -> Driver<Element> {
return self.asDriver { _ -> Driver<Element> in
#if DEBUG
rxFatalError("Somehow driver received error from a source that shouldn't fail.")
#else
return Driver.empty()
#endif
}
}
複製代碼
此方法返回的是一個Driver<Element>
類型的實例,而後調用了self.asDriver
方法,繼續跟蹤進去網絡
public func asDriver(onErrorRecover: @escaping (_ error: Swift.Error) -> Driver<Element>) -> Driver<Element> {
let source = self
.asObservable()
.observeOn(DriverSharingStrategy.scheduler)
.catchError { error in
onErrorRecover(error).asObservable()
}
return Driver(source)
}
複製代碼
從上面的代碼能夠看出,先對源序列調用了observeOn
方法和catchError
方法。observerOn
方法是用來指定線程的,這裏指定了DriverSharingStrategy.scheduler
內部指定的就是主線程,這裏就解決了Driver的執行是在主線程的,咱們能夠隨心所欲的刷新UI,不用擔憂線程問題。catchError
方法是用來處理錯誤信號的,當接收到錯誤信號後,能夠把錯誤信號處理成一個onNext信號發送出去。最後初始化一個Driver
對象返回,Driver
只是一個別名,其實是一個SharedSequence<DriverSharingStrategy, Element>
對象。 進入到SharedSequence
類的初始化方法dom
public struct SharedSequence<SharingStrategy: SharingStrategyProtocol, Element> : SharedSequenceConvertibleType {
let _source: Observable<Element>
init(_ source: Observable<Element>) {
self._source = SharingStrategy.share(source)
}
......
}
複製代碼
在初始化的時候調用了SharingStrategy.share(source)
這個方法,那SharingStrategy
是什麼類型呢,它是SharedSequence
類型的一個類類型參數,而後經過public typealias Driver<Element> = SharedSequence<DriverSharingStrategy, Element>
這行代碼能夠知道SharingStrategy
其實就是DriverSharingStrategy
類型。因此咱們進入到DriverSharingStrategy
結構體,代碼以下:async
public struct DriverSharingStrategy: SharingStrategyProtocol {
public static var scheduler: SchedulerType { return SharingScheduler.make() }
public static func share<Element>(_ source: Observable<Element>) -> Observable<Element> {
return source.share(replay: 1, scope: .whileConnected)
}
}
複製代碼
能夠看到執行了share
方法,這個方法的功能就是共享狀態,防止重複發送請求。 到此,咱們就知道了Driver
是如何解決一些可能存在的問題的。ide
若是咱們的序列知足以下特徵,就可使用它:函數
MainScheduler
)shareReplayLatestWhileConnected
)