RxSwift + MVVM 項目實戰(zhàn)

RxSwift 是什么? 為什么要引入它?它有什么優(yōu)點儒飒、好處呢者蠕?

函數式編程:利用高階函數,即將函數作為其它函數的參數佩番。

響應式編程:關注于數據流及變化的傳播众旗。

概述

見名知意,RxSwift 是在 Apple 推出 Swift 后,針對 Swift 語言 ReactiveX 推出 Reactive Extensions 系列一個實現庫;除此之外趟畏,ReactiveX 還推出了 RxJava贡歧,RxAndroid,RxPHP 等蘊含類似思想的框架赋秀。

為什么要學習RxSwift?

我們知道 C 語言的面向過程,Objective-C利朵、C++ 面向對象編程, Java 的 Spring 框架提出了面向切面編程的思想,學習 RxSwift 不是學習如何使用第三方庫,而是學習一種編程思想--函數響應式編程;

Target Action

傳統實現方法:

button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
func buttonTapped() {
    print("button Tapped")
}

通過 Rx 來實現:

button.rx.tap
    .subscribe(onNext: {
        print("button Tapped")
    })
    .disposed(by: disposeBag)

你不需要使用 Target Action猎莲,這樣使得代碼邏輯清晰可見绍弟。

代理

傳統實現方法:

class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self
    }
}

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print("contentOffset: \(scrollView.contentOffset)")
    }
}

通過 Rx 來實現:

class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()

        scrollView.rx.contentOffset
            .subscribe(onNext: { contentOffset in
                print("contentOffset: \(contentOffset)")
            })
            .disposed(by: disposeBag)
    }
}

你不需要書寫代理的配置代碼,就能獲得想要的結果著洼。

通知

傳統實現方法:

var ntfObserver: NSObjectProtocol!

override func viewDidLoad() {
    super.viewDidLoad()

    ntfObserver = NotificationCenter.default.addObserver(
          forName: .UIApplicationWillEnterForeground,
          object: nil, queue: nil) { (notification) in
        print("Application Will Enter Foreground")
    }
}

deinit {
    NotificationCenter.default.removeObserver(ntfObserver)
}

通過 Rx 來實現:

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.rx
        .notification(.UIApplicationWillEnterForeground)
        .subscribe(onNext: { (notification) in
            print("Application Will Enter Foreground")
        })
        .disposed(by: disposeBag)
}

你不需要去管理觀察者的生命周期樟遣,這樣你就有更多精力去關注業(yè)務邏輯。

4身笤、等待多個并發(fā)任務完成后處理結果

例如豹悬,需要將兩個網絡請求合并成一個,

通過 Rx 來實現:

/// 用 Rx 封裝接口
enum API {

    /// 取得老師的詳細信息
    static func teacher(teacherId: Int) -> Observable<Teacher> { ... }

    /// 取得老師的評論
    static func teacherComments(teacherId: Int) -> Observable<[Comment]> { ... }
}
/// 同時取得老師信息和老師評論
Observable.zip(
      API.teacher(teacherId: teacherId),
      API.teacherComments(teacherId: teacherId)
    ).subscribe(onNext: { (teacher, comments) in
        print("獲取老師信息成功: \(teacher)")
        print("獲取老師評論成功: \(comments.count) 條")
    }, onError: { error in
        print("獲取老師信息或評論失敗: \(error)")
    })
    .disposed(by: disposeBag)

這樣你可用寥寥幾行代碼來完成相當復雜的異步操作液荸。

Why use RxSwift?

A vast majority of the code we write involves responding to external events. When a user manipulates a control, we need to write an @IBAction handler to respond. We need to observe notifications to detect when the keyboard changes position. We must provide closures to execute when URL sessions respond with data. And we use KVO to detect changes to variables. All of these various systems makes our code needlessly complex. Wouldn't it be better if there was one consistent system that handled all of our call/response code? Rx is such a system.
RxSwift is the official implementation of Reactive Extensions (aka Rx), which exist for most major languages and platforms.

翻譯:

我們編寫的絕大多數代碼涉及對外部事件的響應瞻佛。當一個用戶操作控制,我們需要寫一個@IBAction處理器響應娇钱。當鍵盤改變位置時涤久,我們需要觀察通知來檢測涡尘。當URL會話響應數據時,必須提供閉包來執(zhí)行响迂。我們使用KVO的變化來檢測變量考抄。所有這些不同的系統使我們的代碼不必要的復雜。如果有一個一致的系統處理我們所有的呼叫/響應代碼蔗彤,這不是更好嗎川梅?Rx就是這樣一個系統。
RxSwift是官方實現的Reactive擴展正(又名Rx)然遏,存在的最主要的語言和平臺贫途。

RxSwift的優(yōu)點

  • Composable 可組合,在設計模式中有一種模式叫做組合模式待侵,你可以方便的用不同的組合實現不同的類
  • Reusable 代碼可重用丢早,原因很簡單,對應RxSwift秧倾,就是一堆Obserable
  • Declarative 響應式的怨酝,因為狀態(tài)不可變,只有數據變化
  • Understandable and concise 簡潔那先,容易理解农猬, 因為它抽象的了異步編程,使我們統一了代碼風格售淡。
  • Stable 穩(wěn)定斤葱,因為RxSwift寫出的代碼,單元測試時分方便
  • Less stateful “無”狀態(tài)性揖闸,因為對于響應式編程揍堕,你的應用程序就是一堆數據流
  • Without leaks 沒有泄漏,因為資源管理非常簡單

響應式編程:ReactiveCocoa vs RxSwift 選誰好?

RAC是一個已經有著3年歷史的項目汤纸,從Objective-C時期開始衩茸,后來從3.0開始支持了swift(可以通過bridge在OC下使用),接著就完全停止了在Objective-C上的維護蹲嚣。RxSwift項目的時間短一些只有幾個月(作者寫的時間是15年),但是社區(qū)似乎充滿了動力祟牲。關于RxSwift有一件重要的事是項目是按照 ReactiveX這個組織的規(guī)定下開發(fā)的隙畜,并且所有其他語言的Rx項目也是一樣。如果學會了如何使用RxSwift说贝,再去學習Rx.Net, RxJava 或者 RxJS就是小菜一碟议惰,只是語言語法上的差異。這真的就是learn once, apply everywhere.


![WX20200701-122246@2x.png](https://upload-images.jianshu.io/upload_images/5142622-f640a492764bc0f5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
如果項目中有oc,就用ReactiveCocoa乡恕;
要是用沒有言询,就用RxSwift咯

RxSwift comprises five separate components depending on eachother in the following way:

RxSwift comprises.png

1.png

1俯萎、RxSwift 中文文檔
https://github.com/beeth0ven/RxSwift-Chinese-Documentation
2、官網 Observable
http://reactivex.io/documentation/observable.html
3运杭、官網 Operators
http://reactivex.io/documentation/operators.html
4夫啊、官網 Subject
http://reactivex.io/documentation/subject.html
5、官網 Single
http://reactivex.io/documentation/single.html
6辆憔、官網 Scheduler
http://reactivex.io/documentation/scheduler.html

RxSwift 核心流程

  1. 觀察者(Observer)
  2. 被觀察者(Observable)
  3. 訂閱者(Subscriber) 事件的最終處理者
  4. 管道(Sink) Observer 和 Observable 溝通的橋梁

1 創(chuàng)建信號
2 訂閱信號
3 發(fā)送信號

  /// MARK: - testDemo1
  private func testDemo1() {
    
    // 1.創(chuàng)建信號 observable
    requestObservable = Observable.create { (subscriber) -> Disposable in
      // 3.發(fā)送消息
      self.count += 1
      subscriber.on(.next("哈哈哈哈哈??\(self.count)"))
      subscriber.on(.completed)
      
      return Disposables.create()
    }
    
    // 2.訂閱信號
    requestObservable?.subscribe(onNext: { (text) in
      print("=== \(text)")
      self.mapPassword.text = text
    }).dispose()
  }

我們重點關注:

create 閉包什么時候執(zhí)行撇眯,
subscribe 閉包又是什么時候執(zhí)行的


WX20200701-122246@2x.png

Observable 繼承體系

Observable的核心函數:

subscribe 訂閱操作, Observable 和 Observer 通過訂閱建立聯系,
Observable 好比水源, Observer 好比水龍頭(永遠開著的水龍頭)虱咧, 訂閱的過程就是在Observable 和 Observer 之間建立管道熊榛, 一旦建立管道即是永久性的,只要水源有誰腕巡, 水龍頭就會有水玄坦。
run 對用戶不可見,隱藏了大量的實現細節(jié)绘沉, 這個函數就是建立水管的過程
asObservable: 這個協議的存在使得Observable 的定義變得更加廣泛煎楣。

首先說一下繼承體系: AnonymousObservable -> Producer -> Observable -> ObservableType -> ObservableConvertibleType
每一層都只處理一點點事情,剩下的交給下一層處理

ObservableConvertibleType: 完全的抽象

ObservableType: 處理subscribe

Observable: 處理 asObservable

Producer: 重載subscribe

AnonymousObservable: 處理run

也就是說如果說如果我們要自定義一個Observable的話梆砸,通常只需要繼承Producer转质, 并實現run方法。AnonymousObservable做的事情也不多帖世,實現run方法休蟹,作為create閉包的持有者。

run方法涉及另外一個類AnonymousObservableSink,Sink作為Observer 和 Observable的銜接的橋梁日矫,之前還在想為什么叫做ObservableSink赂弓,現在想明白了。Sink本身遵守ObseverType協議哪轿,與此同時實現了run方法盈魁,雖然沒有實現subscribe方法,但是已經足夠了窃诉,這樣sink從某種程度來說也是Observable杨耙,通過sink就可以完成從Observable到Obsever的轉變。

Observer 繼承體系

核心語句

// Producer Class
  override func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
        if !CurrentThreadScheduler.isScheduleRequired {
            // The returned disposable needs to release all references once it was disposed.
            let disposer = SinkDisposer()
            let sinkAndSubscription = 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
            }
        }
    }

我們來看看run的邏輯

   // AnonymousObservable Class
    override func run<O : ObserverType>(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element {
        let sink = AnonymousObservableSink(observer: observer, cancel: cancel)
        let subscription = sink.run(self)
        return (sink: sink, subscription: subscription)
    }

在進行訂閱的過程中飘痛,Producer 的subscribe :方法里面珊膜,通過
let sinkAndSubscription = self.run(observer, cancel: disposer)這個核心語句
運行run()方法,通過實例AnonymousObservable實現的run()方法宣脉,調取AnonymousObservableSinkrun()方法车柠,通過參數的形式,將自己傳給了Sink 類,observer 竹祷;因為Sink類中持有一個observer 谈跛。 AnonymousObservableSink 的方法里面有

    func run(_ parent: Parent) -> Disposable {
        return parent._subscribeHandler(AnyObserver(self))
    }

終于觸發(fā)subscribeHandler了,這里的subscribeHandler就是之前最開始的閉包塑陵。

Observable<String>.create { observer -> Disposable in
      observer.onNext("hello")
      return Disposables.create()
  }

稍微解釋一下整個過程分為兩個階段:

Obsevable 構建階段感憾,這里使用create構造方法構造Obsevable,還有其他各種各樣的構造方法猿妈,這里不一一贅述吹菱。
subscribe 階段, Obsevable.subscribe ---> Obsevable.run ----> Sink.run ----> AnyObserver.On ----> Sink.on ----> Observer.On ---> Observer.OnCore ----> eventHandler

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末彭则,一起剝皮案震驚了整個濱河市鳍刷,隨后出現的幾起案子,更是在濱河造成了極大的恐慌俯抖,老刑警劉巖输瓜,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異芬萍,居然都是意外死亡尤揣,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門柬祠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來北戏,“玉大人,你說我怎么就攤上這事漫蛔∈扔” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵莽龟,是天一觀的道長蠕嫁。 經常有香客問我,道長毯盈,這世上最難降的妖魔是什么剃毒? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮搂赋,結果婚禮上赘阀,老公的妹妹穿的比我還像新娘。我一直安慰自己脑奠,他們只是感情好基公,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捺信,像睡著了一般酌媒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上迄靠,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天秒咨,我揣著相機與錄音,去河邊找鬼掌挚。 笑死雨席,一個胖子當著我的面吹牛,可吹牛的內容都是我干的吠式。 我是一名探鬼主播陡厘,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼特占!你這毒婦竟也來了糙置?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤是目,失蹤者是張志新(化名)和其女友劉穎谤饭,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體懊纳,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡揉抵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了嗤疯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冤今。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖茂缚,靈堂內的尸體忽然破棺而出戏罢,到底是詐尸還是另有隱情,我是刑警寧澤阱佛,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布帖汞,位于F島的核電站,受9級特大地震影響凑术,放射性物質發(fā)生泄漏翩蘸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一淮逊、第九天 我趴在偏房一處隱蔽的房頂上張望催首。 院中可真熱鬧,春花似錦泄鹏、人聲如沸郎任。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舶治。三九已至分井,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間霉猛,已是汗流浹背尺锚。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留惜浅,地道東北人瘫辩。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像坛悉,于是被迫代替她去往敵國和親伐厌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345