FRP 與 RxSwift基礎(chǔ)

本文主要嘗試闡述

  • 什么是 FRP
  • RxSwift的幾個(gè)基礎(chǔ)概念
  • RxSwift的最簡(jiǎn)單使用流程
  • RxSwift是怎么通過(guò)基本流程來(lái)實(shí)現(xiàn)事件響應(yīng)的

FRP

FRP全稱為 Functional Reactive Programming, 即函數(shù)響應(yīng)式編程哪痰。


image.png
函數(shù)式編程

強(qiáng)調(diào)每個(gè)程序都能夠被反復(fù)分解為越來(lái)越小的模塊單元,而所有這些塊可以通過(guò)函數(shù)裝配起來(lái)碧信,以定義一個(gè)完整的程序润梯。

響應(yīng)式編程

在響應(yīng)式編程當(dāng)中赠橙,a:=b+c聲明的是一種綁定關(guān)系耽装。(a與b狡耻、c綁定起來(lái)了借浊,所以b减宣、c的變化會(huì)影響a界轩,這也就是所謂【變化傳播】)

RxSwift 的 基礎(chǔ)概念

可觀察序列 Observable

Observable 是RxSwift 框架的核心概念画饥,它是RxSwift的數(shù)據(jù)源
請(qǐng)求接口的網(wǎng)絡(luò)數(shù)據(jù),可以定義成一個(gè)Observable a
請(qǐng)求數(shù)據(jù)庫(kù)的數(shù)據(jù)浊猾,也可以定義成一個(gè)Observable b
而我們的界面顯示的數(shù)據(jù) c 可以綁定到 a + b的數(shù)據(jù)流上抖甘,即 c := a + b
我們可以想象到 c 也是一個(gè) Observable ,
而 + 和 := 操作 在RxSwift中定義了相應(yīng)的函數(shù)式
在RxSwift中只要實(shí)現(xiàn)了 ObservableType協(xié)議的類都可以認(rèn)為是 Observable葫慎,
ObservableType的定義如下

public protocol ObservableType: ObservableConvertibleType {
  func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable 
  where Observer.Element == Element
}

從協(xié)議的定義可以認(rèn)為衔彻,要成為一個(gè)可觀察序列,只要實(shí)現(xiàn)subscribe方法即可偷办,
而subscribe方法有一個(gè)Observer類型的參數(shù)艰额,并且Observer需要實(shí)現(xiàn)ObserverType協(xié)議,下面我們會(huì)介紹觀察者Observer

觀察者 Observer

觀察者椒涯,見(jiàn)名知意我們可以認(rèn)為柄沮,它有一個(gè)被觀察者,即Observable,通過(guò)普通的觀察者模式祖搓,我們應(yīng)該知道狱意,一般觀察者會(huì)定義一些行為來(lái)相應(yīng)Observable的變化。我們來(lái)看看觀察者的定義

public protocol ObserverType {
    /// The type of elements in sequence that observer can observe.
    associatedtype Element

    @available(*, deprecated, message: "Use `Element` instead.")
    typealias E = Element

    /// Notify observer about sequence event.
    ///
    /// - parameter event: Event that occurred.
    func on(_ event: Event<Element>)
}

與Observable類似拯欧,要成為一個(gè)觀察者详囤,只要實(shí)現(xiàn)ObserverType協(xié)議即可。

以上介紹了RxSwift的兩個(gè)基礎(chǔ)概念, Observable和Observer
那么Rxswift是怎么通過(guò)這兩個(gè)對(duì)象來(lái)處理業(yè)務(wù)的呢镐作?

RxSwift的基本使用流程

// 1. 創(chuàng)建序列
let observable = Observable<Int>.create { (observer: AnyObserver<Int>) -> Disposable in    
        
}

// 2. 訂閱序列
let disposable = observable.subscribe(onNext: { (value) in
            
})

// 3. 資源回收
disposable.disposed(by: disposeBag)

RxSwift 處理事務(wù)的流程如上

  • 創(chuàng)建一個(gè)可觀察序列
  • 通過(guò)觀察者訂閱可觀察序列
  • 將訂閱的資源放入資源回收包中統(tǒng)一回收

舉個(gè)例子纬纪,使用RxSwift來(lái)處理button的點(diǎn)擊事件

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

為了驗(yàn)證流程, 我們需要查看button.rx.tap是否創(chuàng)建了一個(gè)Observable滑肉,來(lái)帶 UIControl + Rx.swift文件包各,查看到源碼如下

public func controlEvent(_ controlEvents: UIControl.Event) -> ControlEvent<()> {
        let source: Observable<Void> = Observable.create { [weak control = self.base] observer in
                MainScheduler.ensureRunningOnMainThread()

                guard let control = control else {
                    observer.on(.completed)
                    return Disposables.create()
                }

                let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { _ in
                    observer.on(.next(()))
                }

                return Disposables.create(with: controlTarget.dispose)
            }
            .takeUntil(deallocated)
        // ControlEvent其實(shí)也是一個(gè)特殊的可觀察序列
        return ControlEvent(events: source)
    }

即button的點(diǎn)擊事件處理的第一步,確實(shí)創(chuàng)建了一個(gè)Observable靶庙。同時(shí)我們要對(duì)點(diǎn)擊事件處理的代碼问畅,也確實(shí)是寫(xiě)在觀察者訂閱序列的時(shí)候,并且也必須得通過(guò)disposed(by: disposeBag)方法回收資源六荒。

RxSwift是怎么通過(guò)基本流程來(lái)實(shí)現(xiàn)事件響應(yīng)的

首先我們來(lái)分析护姆,在UIKit中點(diǎn)擊事件的響應(yīng)流程,即target-action模式

  1. 定義一個(gè)響應(yīng)函數(shù)
  2. 通過(guò) button.addTarget將響應(yīng)函數(shù)作為button的事件處理函數(shù)

然后我們注意到掏击,在rxswift的button.rx.tap的實(shí)現(xiàn)中卵皂,Observable的創(chuàng)建過(guò)程中,有下面這段代碼塊:

let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { _ in
    observer.on(.next(()))
}

在ControlTarget類文件中我們會(huì)看到UIKit的target-action模式砚亭,并且可以知道當(dāng)點(diǎn)擊事件發(fā)生時(shí)就會(huì)執(zhí)行ControlTarget初始化時(shí)傳進(jìn)去的閉包灯变,即執(zhí)行 observer.on(.next(()))

通過(guò)以上分析我們知道,當(dāng)點(diǎn)擊事件發(fā)生時(shí)就會(huì)執(zhí)行 observer.on(.next(()))捅膘,但是我們處理事件的業(yè)務(wù)卻是寫(xiě)在Observable對(duì)象的subscribe函數(shù)的參數(shù)閉包下面添祸。
因此我們分析這兩個(gè)閉包是否有關(guān)系呢?
或者我們猜測(cè)他們肯定有關(guān)系,才能保證流程是正確的寻仗。
同時(shí)我們會(huì)有一個(gè)疑問(wèn)刃泌,observer是在哪里創(chuàng)建并且傳入到Observable中去的呢?

分析RxSwift的源碼署尤,可以發(fā)現(xiàn)

extension ObservableType {
    // MARK: create
    public static func create(_ subscribe: @escaping (AnyObserver<Element>) -> Disposable) -> Observable<Element> {
        return AnonymousObservable(subscribe)
    }
}

final private class AnonymousObservable<Element>: Producer<Element> {
    typealias SubscribeHandler = (AnyObserver<Element>) -> Disposable

    let _subscribeHandler: SubscribeHandler

    init(_ subscribeHandler: @escaping SubscribeHandler) {
        self._subscribeHandler = subscribeHandler
    }

    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)
    }
}

class Producer<Element> : Observable<Element> {
    override init() {
        super.init()
    }

    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
            }
        }
    }

    func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
        rxAbstractMethod()
    }
}
  1. Observable create方法會(huì)創(chuàng)建一個(gè)內(nèi)部私有類AnonymousObservable的對(duì)象
  2. AnonymousObservable繼承自Producer類耙替,而Producer實(shí)現(xiàn)了ObservableType協(xié)議要求的subscribe方法,subscribe方法會(huì)接收到一個(gè)觀察者observer曹体。
  3. 通過(guò)subscribe的參數(shù)俗扇,可以知道我們使用的訂閱函數(shù)和Producer的subscribe并不是同一個(gè)函數(shù)

然后分析我們觀察者訂閱可觀察序列時(shí)調(diào)用的subscribe函數(shù)

public func subscribe(onNext: ((Element) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
        -> Disposable {
            let disposable: Disposable
            
            if let disposed = onDisposed {
                disposable = Disposables.create(with: disposed)
            }
            else {
                disposable = Disposables.create()
            }
            
            #if DEBUG
                let synchronizationTracker = SynchronizationTracker()
            #endif
            
            let callStack = Hooks.recordCallStackOnError ? Hooks.customCaptureSubscriptionCallstack() : []
            // 這里創(chuàng)建了一個(gè)observer
            let observer = AnonymousObserver<Element> { event in
                
                #if DEBUG
                    synchronizationTracker.register(synchronizationErrorMessage: .default)
                    defer { synchronizationTracker.unregister() }
                #endif
                
                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
            )
    }

發(fā)現(xiàn),原來(lái)觀察者是通過(guò)訂閱的時(shí)候創(chuàng)建的混坞,并且在return語(yǔ)句時(shí)通過(guò)self.asObservable().subscribe(observer)傳給Observable狐援,這個(gè)self應(yīng)該是AnonymousObservable的對(duì)象,這個(gè)subscribe即為Producer類定義的subscribe方法究孕。

至此啥酱,我們可以確定,observer.on(.next(()))方法確實(shí)是調(diào)用的訂閱時(shí)subscribe的onNext閉包厨诸。

總結(jié)
Observer: onNext()
Observable -> AnonymousObservable -> Producer : subscribe(observer)
ObservableType: subscribe(onNext: ()->()), create() -> AnyAnonymousObservable
  • Observable.create 可以拿到一個(gè)可觀察序列镶殷,并且,可以通過(guò)閉包參數(shù)observer來(lái)發(fā)送數(shù)據(jù)
  • Observable.create 創(chuàng)建的Observable其實(shí)是AnonymousObservable類型的(Observable的創(chuàng)建方法還有其他的微酬,它們其實(shí)是生成了不同的ObservableType的實(shí)現(xiàn)類)绘趋,創(chuàng)建的各種Observable其實(shí)都是繼承自Producer
  • 觀察者訂閱可觀察序列,其實(shí)是ObservableType協(xié)議的默認(rèn)實(shí)現(xiàn)颗管,并且我們只要提供處理數(shù)據(jù)的閉包就行陷遮,observer在ObservableType默認(rèn)實(shí)現(xiàn)的subscribe方法中就自動(dòng)創(chuàng)建,并且會(huì)將我們提供的閉包給到創(chuàng)建的observer
  • ObservableType協(xié)議的默認(rèn)實(shí)現(xiàn)會(huì)將創(chuàng)建的observer傳遞給自己的實(shí)現(xiàn)類的對(duì)象
  • Observable一旦發(fā)送數(shù)據(jù)垦江,observer就會(huì)執(zhí)行我們subscribe時(shí)提供的閉包

參考資料

什么是函數(shù)式編程
函數(shù)響應(yīng)式編程(FRP)思想
RxSwift源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帽馋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子比吭,更是在濱河造成了極大的恐慌绽族,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衩藤,死亡現(xiàn)場(chǎng)離奇詭異吧慢,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)赏表,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)检诗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人瓢剿,你說(shuō)我怎么就攤上這事岁诉。” “怎么了跋选?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵涕癣,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我前标,道長(zhǎng)坠韩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任炼列,我火速辦了婚禮只搁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘俭尖。我一直安慰自己氢惋,他們只是感情好洞翩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著焰望,像睡著了一般骚亿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上熊赖,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天来屠,我揣著相機(jī)與錄音,去河邊找鬼震鹉。 笑死俱笛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的传趾。 我是一名探鬼主播迎膜,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼浆兰!你這毒婦竟也來(lái)了星虹?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤镊讼,失蹤者是張志新(化名)和其女友劉穎宽涌,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蝶棋,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡卸亮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了玩裙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兼贸。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖吃溅,靈堂內(nèi)的尸體忽然破棺而出溶诞,到底是詐尸還是另有隱情,我是刑警寧澤决侈,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布螺垢,位于F島的核電站,受9級(jí)特大地震影響赖歌,放射性物質(zhì)發(fā)生泄漏枉圃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一庐冯、第九天 我趴在偏房一處隱蔽的房頂上張望孽亲。 院中可真熱鬧,春花似錦展父、人聲如沸返劲。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)篮绿。三九已至孵延,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間搔耕,已是汗流浹背隙袁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工痰娱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留弃榨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓梨睁,卻偏偏與公主長(zhǎng)得像鲸睛,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子坡贺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354