RxSwift源碼分析(四)-特徵序列Driver

基本介紹

  • Driver能夠說是最複雜的 trait,它的目標是提供一種簡便的方式在 UI 層編寫響應式代碼。
  • 若是咱們的序列知足以下特徵,就可使用它:
    • 不會產生 error 事件
    • 必定在主線程監聽(MainScheduler
    • 共享狀態變化(shareReplayLatestWhileConnected

爲何要使用 Driver?

  • Driver 最常使用的場景應該就是須要用序列來驅動應用程序的狀況了,好比:經過 CoreData 模型驅動 UI,使用一個 UI 元素值(綁定)來驅動另外一個 UI 元素值。
  • 與普通的操做系統驅動程序同樣,若是出現序列錯誤,應用程序將中止響應用戶輸入。
  • 在主線程上觀察到這些元素也是極其重要的,由於 UI 元素和應用程序邏輯一般不是線程安全的。
  • 此外,使用構建 Driver 的可觀察的序列,它是共享狀態變化。

使用樣例

咱們來實現這樣一個需求:根據一個輸入框的關鍵字,來請求數據,而後將獲取到的結果同時綁定到label和button上。api

  1. 普通序列的實現
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()
    })
}
複製代碼

如上面的代碼註釋所示:爲了解決一些可能存在的問題,咱們都須要去加額外的加一些處理,保證程序正常運行,這是很是不友好的,由於對於一個大型的項目來講,若是都這麼幹也太麻煩了,並且容易遺漏出錯。因此咱們須要用另一種方式來實現一樣的功能。安全

  1. 經過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

總結

若是咱們的序列知足以下特徵,就可使用它:函數

  • 不會產生 error 事件
  • 必定在主線程監聽(MainScheduler
  • 共享狀態變化(shareReplayLatestWhileConnected
相關文章
相關標籤/搜索