1. 程式人生 > Android開發 >mojito: 麻煩給我的愛人來一份 RxSwift

mojito: 麻煩給我的愛人來一份 RxSwift

學過 Swift 的 同學都知道, RxSwift 宛如 周董的 mojito

開始微醺

再者上頭

為什麼要學習 RxSwift ?

卡蜜爾說過

優雅,永不過時


麻煩給我的愛人來一份 RxSwift

RxSwift 是 Rx 系列的 Swift 版本,相較於 OC 版的 ReactiveCocoa

它們有著異曲同工之妙 函式響應式程式設計(FRP)

什麼是 函式響應式程式設計 ?


函式式:

函式語言程式設計的核心思想是 stateless,無狀態。函式本身並不關心外界輸入的值

它只是在函式內部,將輸入的值 和 輸出的值 完成一種對映,即 input => output

比如:

func changeNum(input: Int) -> Int {
    return
input * 3 } // changeNum 並不會對input 產生改變,只是將運算之後的值 輸出 複製程式碼

無狀態 意味著函式本身,不會改變外部的狀態,也不會改變輸入的值的狀態


再比如

Q:將陣列 [1,2,3,4] 裡的元素 都乘以2,返回新的陣列

那麼一般的做法可能是:

指令式程式設計

let array = [1,4]
var newArray: [Int] = []

for item in array {
    var num = item
    num *= 2
    newArray.append(num)
} 
// [2,4,6,8]
複製程式碼

指令式程式設計傾向於怎麼做

,具體是怎麼把每個數都 * 2 的,那麼這裡 涉及到了 可變陣列 newArray

如果某一個時刻,newArray 被某個地方改變了,都會達到 非預期的效果


那麼函式語言程式設計會怎麼做呢?

let array = [1,4]

let newArray = array.compactMap {
    return $0 * 2
}
// [2,8]
複製程式碼

函式語言程式設計申明式程式設計的 思想大體一致

它們都只關注 做什麼,而不是上面的 怎麼做?


函式語言程式設計:傾向於做什麼,省去其繁瑣的過程,一種更為 安全,直觀,易懂的程式設計方式


響應式:

一種抽象的事件流非同步程式設計方法

比如:使用者點選一個按鈕,傳送網路請求,並將結果展示在label 上

這裡網路請求是非同步

想要展示在label 上,就要拿到 網路請求的回撥,進一步展示

形成事件流寫法就是


button.rx.tap.subscribe(onNext: {              // 點選按鈕
    HomeApi.getTitle().asObservable() // 發起網路請求
    .map { (title) -> String in       // 拿到回撥進行 map
    ("我是 \(title)")
    } 
    .bind(to: (titleLabel?.rx.text)!) // 繫結給 label
    .disposed(by: rx.disposeBag)      // 管理生命週期
})
複製程式碼

這麼一個較為複雜的操作,且包含非同步操作的流程

在 RxSwift 的 調整之後,是不是更為 簡單易懂 ? 事件的分發以及維護,可以在一個地方完成

大大提高了 程式碼的可讀性,以及維護成本

整個事件流的過程 如下:

我們不用去關心 序列中的 每一個元素,是非同步的 還是同步的,執行緒是否安全

只有當我們點選按鈕傳送訊號 之後 ,程式碼塊內的函式體才會執行,整個一系列的事件流才會產生

這使得我們更加面向業務邏輯

而不是每一步的具體操作


那麼具體 RxSwift 是怎麼做到的呢?

喝完 mojito 你就知道了


我喜歡閱讀它時緊皺的眉頭

對於初學者來說

RxSwift 的學習曲線確實很陡,它詮釋了什麼是面向協議程式設計

過程雖然晦澀

但道阻且長

真正的大師永遠懷著一顆學徒的心


rx

在RxSwift 的世界裡,萬物皆 rx,到處是 序列(sequence)

聽著像不像 iOS 的萬物皆物件

是的沒錯,我們來看一下rx Reactive 的定義,首先引入眼簾的是 一個 叫 ReactiveCompatible 的協議

public protocol ReactiveCompatible {
    # 關聯協議
    associatedtype ReactiveBase 

    # rx 是  Reactive 型別,並將 ReactiveBase 傳入
    static var rx: Reactive<ReactiveBase>.Type { get set }

    var rx: Reactive<ReactiveBase> { get set }
}
複製程式碼

Reactive 中 還對 ReactiveCompatible進行了 協議的拓展,在這個擴充套件中,通過呼叫rx,返回的是

Reactive 型別 或者Reactive例項

extension ReactiveCompatible {
    # Reactive 型別
    public static var rx: Reactive<Self>.Type {
        get {  return Reactive<Self>.self }
    }
    # Reactive 例項
    public var rx: Reactive<Self> {
        get {  return Reactive(self) }
    }
}
複製程式碼

在看一下 Reactive 的 實現,是一個 包含引數泛型 Base 的結構體

public struct Reactive<Base> {
    public let base: Base
    # 將 Reactive 的初始化呼叫者 設定為 base
    public init(_ base: Base) {
        self.base = base
    }
}
複製程式碼

如上文中 點選按鈕的 tap,即 button.rx.tap,型別就是 UIButton 型別,將 UIButton 的例項 設定為 base

那麼想 實現 萬物皆rx,只需要簡單的一步

extension NSObject: ReactiveCompatible { }
複製程式碼

這樣就可以讓所有繼承於 NSObjce 的物件,都遵循 ReactiveCompatible 協議,即 萬物皆rx


Observable

Observable 意味著,可被觀察的,也就是可觀察序列,什麼是序列呢?

我理解的就是 具備 發出事件能力的 的一種訊號


比如:

肚子餓了 -> 吃飯

肚子餓了 可以作為一個 可觀察序列,當我們大腦感知到 肚子餓了,就可以執行 去吃飯的操作


TextField 輸入 -> 顯示

TextField 輸入操作可以作為一個序列,可以監聽到 輸入的內容


接下來

就開始除錯 mojito 了

看一個訂閱過程:

# 建立
let observable = Observable<String>.create { (observe) -> Disposable in
    # 傳送
    observe.onNext("mojito")
    return Disposables.create()
}
# 訂閱
observable.subscribe(onNext: { text in
    print(text)
}).disposed(by: rx.disposeBag)

// print "mojito"
複製程式碼


除錯開始

Observable 可觀察序列

  • step1
# Observable 繼承於  ObservableType
public class Observable<Element> : ObservableType {
    # 資源的引用計數 +1
    init() {
        _ = Resources.incrementTotal()
    }
     # 提供被訂閱的能力,由子類實現
    public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
        rxAbstractMethod()
    }
     # 將 Observable 類轉為 Observable 例項
    public func asObservable() -> Observable<Element> {
        return self
    }
     # 資源的引用計數 -1
    deinit {
        _ = Resources.decrementTotal()
    }
}
複製程式碼

可是這裡並沒有看到序列的建立,但是可以看到一個 繼承關係: Observable<Element> : ObservableType

進入 ObservableType

  • step2
# 協議  ObservableType,繼承於 ObservableConvertibleType
public protocol ObservableType: ObservableConvertibleType {
    func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element
}

# ObservableType 擴充套件
extension ObservableType {
    # 提供了一個方法,將遵守 ObservableType 協議的物件 轉為 Observable 實體
    public func asObservable() -> Observable<Element> {
        return Observable.create { o in
            return self.subscribe(o)
        }
    }
}
複製程式碼

這裡還是 沒有看到 訂閱的方法

還發現了 自己的爸爸是個協議, 還有爺爺 ObservableConvertibleType


持著懷疑的態度,你又點進了 ObservableConvertibleType

  • step3
# 也是個協議
public protocol ObservableConvertibleType {

    associatedtype Element
    typealias E = Element
    
    # 定義了一個方法,返回型別 Observable 可觀察序列
    func asObservable() -> Observable<Element>
}

複製程式碼


可惡

既然這條路走不通,只能先不走了

哪裡跌倒

我就躺在哪裡

為了達到萬物皆序列,我們就要想辦法把所有事件轉化為序列,asObservable() 即為 RxSwift 的精髓


Observable.create()

點選 creat ,豁然開朗,原來建立是通過 ObservableType 擴充套件,這也同時證明瞭 OOP 的好處,可擴充套件性強

ObservableType 像是一家名叫 ObservableType 的連鎖公司

它可以在任何地方開個分店

實現自己公司的業務

  • step4
#  ObservableType 的擴充套件
extension ObservableType {
    public static func create(_ subscribe: @escaping (AnyObserver<Element>) -> Disposable) -> Observable<Element> {
        # 返回一個 匿名觀察序列,將 subscribe 逃逸閉包傳入
        return AnonymousObservable(subscribe)
    }
}
複製程式碼

點選 AnonymousObservable 進入

  • step5
# 私有方法,外界無法共享
# AnonymousObservable 繼承於  Producer
final private class AnonymousObservable<Element>: Producer<Element> {
    typealias SubscribeHandler = (AnyObserver<Element>) -> Disposable
    # 定義 閉包屬性
    let _subscribeHandler: SubscribeHandler
    # 將外界傳入的 閉包 儲存
    init(_ subscribeHandler: @escaping SubscribeHandler) {
        self._subscribeHandler = subscribeHandler
    }
    # 重寫 父類 Producer 提供的 run 方法
    override func run<Observer: ObserverType>(_ observer: Observer,cancel: Cancelable) -> (sink: Disposable,subscription: Disposable) where Observer.Element == Element {
        # 初始化匿名管道,傳入一個訂閱者
        let sink = AnonymousObservableSink(observer: observer,cancel: cancel)
        let subscription = sink.run(self)
        return (sink: sink,subscription: subscription)
    }
}
複製程式碼

這裡又來了個 Producer,點選 Producer

  • step6
# Producer 同樣繼承於 Observable
class Producer<Element> : Observable<Element> {
    override init() {
        super.init()
    }

# 這裡涉及到了執行緒,如果  CurrentThreadScheduler 指定了某個執行緒,那麼就會在指定執行緒中 執行 run
# 否則 就會在當前執行緒中 執行 run
# SinkDisposer例項 disposer,用來管理資源釋放
override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
         if !CurrentThreadScheduler.isScheduleRequired {
            // The returned disposable needs to release all references once it was disposed.
            let disposer = SinkDisposer()
            let sinkAndSubscription = self.run(observer,cancel: disposer)
            disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink,subscription: sinkAndSubscription.subscription)
            return disposer
        }
        else {
            return CurrentThreadScheduler.instance.schedule(()) { _ in
                let disposer = SinkDisposer()
                let sinkAndSubscription = self.run(observer,cancel: disposer)
                disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink,subscription: sinkAndSubscription.subscription)
                return disposer
            }
        }
  }
# 抽象方法,子類去實現,也就是匿名序列 AnonymousObservable
func run<Observer: ObserverType>(_ observer: Observer,subscription: Disposable) where Observer.Element == Element {
    rxAbstractMethod()
    }
}
複製程式碼

相信到此,你我都已經微醺了

你問我什麼是 序列

我 指 著 大 海 的 方 向


目前也只能先畫個圖,繼續觀望


小結

  • 小結

    • 我們呼叫父類協議的 creat 方法 ,生成 匿名觀察序列,即Producer 的子類AnonymousObservable
    • AnonymousObservable 儲存外界傳入的 閉包
    • 負責資源管理,引用計數的 是 Observable 抽象類,不實現方法
    • Producer 類 實現 外界 subscribe 方法,並安排執行緒排程
    • 具體的 run,由 AnonymousObservable 實現,父類 Producer不負責

ok ,繼續往下走

subscribe(onNext:) 訂閱

點選 subscribe 進入,可以看到 ObservableType 的擴充套件,提供了 subscribe.onsubscribe.onNext 2個方法

此處省略了 subscribe.on

  • step7
extension ObservableType {
    ...
    
    public func subscribe(onNext: ((Element) -> Void)? = nil,onError: ((Swift.Error) -> Void)? = nil,onCompleted: (() -> Void)? = nil,onDisposed: (() -> Void)? = nil)
        -> Disposable {
            ....
            # 建立一個 匿名訂閱者  AnonymousObserver
            # 對外界傳入的執行閉包 進行儲存
            let observer = AnonymousObserver<Element> { event in
                switch event {
                case .next(let value):
                    onNext?(value)
                case .error(let error):
                    if let onError = onError {
                        onError(error)
                    } else { Hooks.defaultErrorHandler(callStack,error) }
                    disposable.dispose()
                case .completed:
                    onCompleted?()
                    disposable.dispose()
                }
            }
            return Disposables.create(
                self.asObservable().subscribe(observer),disposable
            )
    }
}
複製程式碼

這裡將 外界需要執行的 閉包,即本例中的 print(text),生成 AnonymousObserver 例項,傳入

self.asObservable().subscribe(observer)

也就是說,這個 AnonymousObserver例項,會通過 Producer 呼叫 subscribe

然後由 子類 AnonymousObservable,序列例項去呼叫 run方法


來到文中,step 5 的 run 方法,如下

# 將外界 需要執行的閉包 ,以及 資源銷燬例項 生成的 元祖 傳入  AnonymousObservableSink
# 生成 sink 管道例項,並執行 run 
# 將run 之後生成的例項,賦值給  subscription,並返回 subscription 和  sink

override func run<Observer: ObserverType>(_ observer: Observer,subscription: Disposable) where Observer.Element == Element {
    let sink = AnonymousObservableSink(observer: observer,cancel: cancel)
    let subscription = sink.run(self)
    return (sink: sink,subscription: subscription)
}
複製程式碼

進入 AnonymousObservableSink

  • step8
final private class AnonymousObservableSink<Observer: ObserverType>: Sink<Observer>,ObserverType {
    typealias Element = Observer.Element 
    typealias Parent = AnonymousObservable<Element>
    
    # 呼叫父類 Sink 的初始化方法,傳入  observer 和 cancel,即 管道 AnonymousObservableSink 持有 這2個屬性
    override init(observer: Observer,cancel: Cancelable) {
        super.init(observer: observer,cancel: cancel)
    }

    func on(_ event: Event<Element>) {
        switch event {
        case .next:
            if load(self._isStopped) == 1 {
                return
            }
            self.forwardOn(event)
        case .error,.completed:
            if fetchOr(self._isStopped,1) == 0 {
                self.forwardOn(event)
                self.dispose()
            }
        }
    }
    ### 熟悉的東西有沒有
    # 這裡看到了  _subscribeHandler,也就是 發出的訊號,儲存的閉包
    
    func run(_ parent: Parent) -> Disposable {
        return parent._subscribeHandler(AnyObserver(self))
    }
}

複製程式碼

到這裡,我們就會發現 sink 管道 它很重要

它持有了

要銷燬的例項,發出序列的閉包,執行序列的閉包

這裡的  AnyObserver(self) 是為了 相容傳入 閉包的型別, 本文對應的 是 String
複製程式碼

也就是說,一旦外界開始訂閱序列

那麼 火車序列 就開始發動了,但是 怎麼響應? 下一步往哪開?

這就要看 AnyObserver(self),做了什麼,進入 AnyObserver, 檢視init


 public init<Observer: ObserverType>(_ observer: Observer) where Observer.Element == Element {
    self.observer = observer.on
}
複製程式碼

你會發現這裡的 self.observer 儲存了 自己的 on 方法

也就是儲存的了一個 function

即 會呼叫 step8 中 的 on,然後 去呼叫 父類Sink 的 forwardOn


  • step9
# 父類 Sink
class Sink<Observer: ObserverType> : Disposable {
    final func forwardOn(_ event: Event<Observer.Element>) {
        #if DEBUG
            self._synchronizationTracker.register(synchronizationErrorMessage: .default)
            defer { self._synchronizationTracker.unregister() }
        #endif
        if isFlagSet(self._disposed,1) {
            return
        }
        # 訂閱者
        self._observer.on(event)
    }
}
複製程式碼

在父類 forwardOn中, 由訂閱者執行 on 事件

可是 訂閱者 AnonymousObserver 類有沒有 on 方法,只有 onCore

所以去 AnonymousObserver 的 父類中 ObserverBase 尋找

class ObserverBase<Element> : Disposable,ObserverType {
    private let _isStopped = AtomicInt(0)

    func on(_ event: Event<Element>) {
        switch event {
        case .next:
            if load(self._isStopped) == 0 {
                self.onCore(event)
            }
        case .error,1) == 0 {
                self.onCore(event)
            }
        }
    }
    # 子類實現
    func onCore(_ event: Event<Element>) {
        rxAbstractMethod()
    }
}
複製程式碼

最後 AnonymousObserver 呼叫自己的 onCore 執行 eventHandler 閉包

到此

整個執行的過程算是走完了

關於資源回收的內容,後續文章會寫到


到此

mojito 初體驗 結束

小結

  • 小結
    • 通過 AnonymousObservable 儲存 可觀察序列
    • 通過 AnonymousObserve 儲存 執行閉包
    • 外界開始訂閱,由 Producer 排程執行緒,執行 subscribe
    • 生成 SinkDisposer 以及 observer 例項 元祖
    • 將 元祖 注入 Sink 管道
    • Sink 處理事件,發出訊號,響應序列
    • 資源回收

簡單的流程圖如下


而我的寫法,輕鬆像魔法

有了RxSwift ,日常開發就變得酣暢淋漓了,比如

  • 監聽 tableView 的滾動:
 tableView.rx.contentOffset.subscribe(onNext: { contentOffset in
    /// 修改透明度
 })
.disposed(by: rx.disposeBag)
複製程式碼

  • 監聽textField 輸入
 textField.rx.text.skip(1).subscribe(onNext: { (text) in
    print("輸入的是 : \(text!)")
 })
 .disposed(by: rx.disposeBag)
複製程式碼

  • 按鈕點選
 self.messageBtn.rx.tap.subscribe(onNext: { in
    Navigator.push("")
 })
 .disposed(by: rx.disposeBag)
複製程式碼

  • tableView 繫結資料來源 代理
  # 這裡需要匯入 RxDataSources
  dataSource = RxTableViewSectionedReloadDataSource(configureCell: { (_,tab,indexPath,item) -> UITableViewCell in
    let cell = tab.dequeue(Reusable.settingCell,for: indexPath)
    cell.bind(to: item)
    return cell
  })
  
  # 或者
  let items = Observable.just([
    "Just","Relay","From","Driver","merge"
  ])
  
 items.bind(to: tableView.rx.items) { (tableView,_,element) in
    let cell = self.tableView.dequeue(TestV.normalCell)
    cell?.textLabel?.text = element
    return cell!
  }
  .disposed(by: rx.disposeBag)
複製程式碼

  • tableView 點選代理
 tableView.rx.itemSelected.subscribe(onNext: { indexPath in
   /// doSomething
 })
 .disposed(by: rx.disposeBag)
複製程式碼

  • 配合 HandyJSON 轉model
extension Response {
    func mapHandyJsonModel<T: HandyJSON>(_ type: T.Type) -> T {
        let jsonString = String.init(data: data,encoding: .utf8)
        if let modelT = JSONDeserializer<T>.deserializeFrom(json: jsonString) {
            return modelT
        }
        return JSONDeserializer<T>.deserializeFrom(json: "{\"msg\":\"解析有誤\"}")!
    }
}

extension ObservableType where Element == Response {
    /// 註釋
    public func mapHandyJsonModel<T: HandyJSON>(_ type: T.Type) -> Observable<T> {
        return flatMap { response -> Observable<T> in
            return Observable.just(response.mapHandyJsonModel(T.self))
        }
    }
}

# 配合 Moya 
static func getTopList() -> Observable<HomeResponseModel> {
    return HomeApiProvider.rx.request(.Top).asObservable().mapHandyJsonModel(HomeResponseModel.self)
}
複製程式碼

  • 多個請求合併
 Observable.zip(HomeApi.getTopList(),HomeApi.getRecommondList()).subscribe(onNext: { topResponse,recommodResponse in
    /// 資料處理
  }).disposed(by: self.rx.disposeBag)
複製程式碼

等等....

先介紹一點簡單的用法

後續會慢慢更新


這世界因我讓你不再受折磨

RxSwift 熟悉了之後,會讓我們的程式碼變得 簡潔且優雅

它面向協議程式設計,我們面向業務程式設計

RxSwift 需要慢慢品味

聽一遍 mojito 肯定是不夠的

不說了

聽歌去了~


對了

RxSwift 中文網

沒喝過 mojito 的 就從這裡開始吧~