RXSwift-函數(shù)響應(yīng)式編程思想(FRP)

常用編程范式

大概有四種,如果按照類似繼承圖譜來看的話,應(yīng)該如下圖:

編程范式

函數(shù)式編程(Functional Program,FP)


函數(shù)式編程是使用函數(shù)來編程的一種編程范式

無副作用

實(shí)際上函數(shù)編程需要可觀測的作用,例如把結(jié)果顯示在屏幕上。換句話說與外界的交互不會(huì)發(fā)生在計(jì)算過程中,而是發(fā)生在計(jì)算完成后——將會(huì)推遲副作用并單獨(dú)應(yīng)用

函數(shù)式編程就是編寫非有意副作用的程序,如果需要副作用盡可能的延遲副作用發(fā)生的時(shí)機(jī)。如果既有返回值又有副作用,這種程序就不是函數(shù)式。

引用透明

函數(shù)式程序的輸出只能取決于自己的參數(shù),這就意味著函數(shù)式代碼不能從控制臺(tái)、文件等讀取數(shù)據(jù),既函數(shù)式代碼是引用透明的(不被外界影響到的代碼是引用透明的)。

引用透明代碼的特點(diǎn):

  • 它是獨(dú)立的。不依賴與任何外部設(shè)備,可以在任何上下文中使用它——只需要提供一個(gè)有效的參數(shù)。
  • 它是確定的。在引用透明的代碼中,不會(huì)有意外發(fā)生,結(jié)果可能是錯(cuò)誤的,但是一個(gè)參數(shù)對應(yīng)的結(jié)果肯定是相同的。
  • 它絕對不會(huì)拋出任何的Exception。但是它可能會(huì)拋出錯(cuò)誤(不是異常)——拋出error。例如OOME或者SOE,這些表示代碼有bug,并不是作為程序員的你或是你api的用戶應(yīng)該處理的——當(dāng)然修復(fù)這個(gè)bug肯定是你要做的。
  • 任何時(shí)候它都不會(huì)導(dǎo)致其他代碼意外失敗。例如他不會(huì)改變參數(shù)或者外界數(shù)據(jù),從而導(dǎo)致調(diào)用者發(fā)現(xiàn)自己的數(shù)據(jù)過期或者并發(fā)訪問異常。
  • 它不會(huì)由于外部設(shè)備(數(shù)據(jù)庫、文件系統(tǒng))不可用、太慢或壞掉而崩潰——但是你的程序會(huì)。

優(yōu)勢

  • 函數(shù)式程序更加易于推斷,因?yàn)樗麄冇写_定性。確定的輸入會(huì)有確定的輸出,在許多情況下可以證明程序是正確的,而不是在大量測試后仍然無法確定程序是否會(huì)在意外情況下出錯(cuò)。
  • 函數(shù)式程序更加易于測試。沒有副作用,所以不需要那些經(jīng)常用于在測試?yán)锔綦x程序及外界的mock。
  • 函數(shù)式程序更加模塊化,只關(guān)心輸入和輸出,不用去處理異常,處理副作用,不用關(guān)心上下文變化,并發(fā)等。
  • 函數(shù)式編程讓符合和重新符合更加簡單。基礎(chǔ)函數(shù)組成高級(jí)別函數(shù),因?yàn)橐霉穷^嗎,所以無須修改就可以為其他程序所重用——例如編寫了一個(gè)求和函數(shù),A程序可以用,B程序也可以用。
  • 函數(shù)式程序總是線程安全的,因?yàn)榉乐沽斯蚕頎顟B(tài)的變化。并不意外這所有數(shù)據(jù)都不需要改變,只有共享數(shù)據(jù)才需要。

響應(yīng)式編程(Reactive Programming:RP)


響應(yīng)式編程是一種面向數(shù)據(jù)流和變化傳播的編程范式

這意味著可以在編程語言中很方便地表達(dá)靜態(tài)或動(dòng)態(tài)的數(shù)據(jù)流,而相關(guān)的計(jì)算模型會(huì)自動(dòng)將變化的值通過數(shù)據(jù)流進(jìn)行傳播。

響應(yīng)式編程最初是為了簡化交互式用戶界面的創(chuàng)建和實(shí)時(shí)系統(tǒng)動(dòng)畫的繪制而提出來的一種方法,但它本質(zhì)上是一種通用的編程范式。

以程序開發(fā)過程為例,

  • 程序接收輸入產(chǎn)生輸出。輸出就是對輸入做了一些事的結(jié)果。輸入,轉(zhuǎn)換,輸出,完成。

  • 輸入是應(yīng)用動(dòng)作的全部來源。點(diǎn)擊、鍵盤事件、定時(shí)器事件、GPS時(shí)間、網(wǎng)絡(luò)請求響應(yīng)都算是輸入。這些事件被傳遞到應(yīng)用中,應(yīng)用將他們以某種方式混合,產(chǎn)生了結(jié)果:就是輸出。

  • 輸出通常會(huì)改變應(yīng)用的UI。開關(guān)狀態(tài)變化、列表有了新的元素都是UI變化。也有可能讓磁盤上某個(gè)文件產(chǎn)生變化,或者產(chǎn)生一個(gè)API請求,這都是應(yīng)用的輸出。

應(yīng)用的輸入輸出可以產(chǎn)生很多次。
應(yīng)用打開后,不只是一個(gè)簡單的 輸入→工作→輸出 就構(gòu)成了一個(gè)生命周期。應(yīng)用經(jīng)常有大量的輸入并基于這些輸入產(chǎn)生輸出。

  • 響應(yīng)式編程是一種和事件流有關(guān)的編程模式,關(guān)注導(dǎo)致狀態(tài)值改變的行為事件,一系列事件組成了事件流。
  • 一系列事件是導(dǎo)致屬性值發(fā)生變化的原因。FRP非常類似于設(shè)計(jì)模式里的觀察者模式。
  • RP與普通的函數(shù)式編程相似,但是每個(gè)函數(shù)可以接收一個(gè)輸入值的流,如果其中,一個(gè)新的輸入值到達(dá)的話,這個(gè)函數(shù)將根據(jù)最新的輸入值重新計(jì)算,并且產(chǎn)生一個(gè)新的輸出。這是一種”數(shù)據(jù)流”編程模式。

為什么用響應(yīng)式?

  • 開發(fā)過程中,狀態(tài)以及狀態(tài)之間依賴過多,RAC更加有效率地處理事件流,而無需顯式去管理狀態(tài)。在OO或者過程式編程中,狀態(tài)變化是最難跟蹤,最頭痛的事。這個(gè)也是最重要的一點(diǎn)。
  • 減少變量的使用,由于它跟蹤狀態(tài)和值的變化,因此不需要再申明變量不斷地觀察狀態(tài)和更新值。
  • 提供統(tǒng)一的消息傳遞機(jī)制,將oc中的通知,action,KVO以及其它所有UIControl事件的變化都進(jìn)行監(jiān)控,當(dāng)變化發(fā)生時(shí),就會(huì)傳遞事件和值。
  • 當(dāng)值隨著事件變換時(shí),可以使用map,filter,reduce等函數(shù)便利地對值進(jìn)行變換操作。

函數(shù)響應(yīng)式編程(Functional Reactive Programming:FRP)


響應(yīng)式編程思想為體, 函數(shù)式編程思想為用

函數(shù)響應(yīng)式編程

可以結(jié)合ReactiveCocoa(簡稱RAC)來理解,
RAC結(jié)合了幾種編程風(fēng)格:

函數(shù)式編程(Functional Programming):使用高階函數(shù)。比如函數(shù)用其它函數(shù)作為參數(shù)。

響應(yīng)式編程(Reactive Programming):關(guān)注于數(shù)據(jù)流和變化傳播。

所以,你可能聽說過ReactiveCocoa被描寫敘述為函數(shù)響應(yīng)式編程(FRP)框架。

有句比喻非常好非常形象的對RAC做了總結(jié):

“能夠把信號(hào)想象成水龍頭,僅僅只是里面不是水,而是玻璃球(value),直徑跟水管的內(nèi)徑一樣,這樣就能保證玻璃球是依次排列。不會(huì)出現(xiàn)并排的情況(數(shù)據(jù)都是線性處理的,不會(huì)出現(xiàn)并發(fā)情況)。水龍頭的開關(guān)默認(rèn)是關(guān)的。除非有了接收方(subscriber),才會(huì)打開。

這樣僅僅要有新的玻璃球進(jìn)來,就會(huì)自己主動(dòng)傳送給接收方。

能夠在水龍頭上加一個(gè)過濾嘴(filter)。不符合的不讓通過,也能夠加一個(gè)修改裝置,把球改變成符合自己的需求(map)。

也能夠把多個(gè)水龍頭合并成一個(gè)新的水龍頭(combineLatest:reduce:),這樣僅僅要當(dāng)中的一個(gè)水龍頭有玻璃球出來,這個(gè)新合并的水龍頭就會(huì)得到這個(gè)球。

RXSwift


RxSwift 是 ReactiveX 針對Swift 的實(shí)現(xiàn)。
RxSwift是一個(gè)針對于Swift語言的響應(yīng)式編程框架,旨在使異步操作和事件/數(shù)據(jù)流的實(shí)現(xiàn)變的簡單。

Observables 和 Observers

兩個(gè)基本概念:Observable和Observer。

  • Observable是發(fā)出變化通知的對象。
  • Observer是監(jiān)聽Observable的對象。當(dāng)Observable變化時(shí),Observer會(huì)收到通知。

可以有多個(gè)Observer監(jiān)聽同一個(gè)Observable。當(dāng)Observable發(fā)生變化時(shí),會(huì)通知它所有的Observer。

DisposeBag

DisposeBag是用來處理 ARC 和內(nèi)存管理的。 DisposeBag是Observer 對象的一個(gè)虛擬包,當(dāng)Observer的父對象被釋放時(shí),這個(gè)虛擬包會(huì)被丟棄。

當(dāng)帶有DisposeBag屬性的對象的 deinit()方法被調(diào)用時(shí),DisposeBag會(huì)清空,并且每一個(gè)用完即丟棄(disposable)的Observer 會(huì)自動(dòng)取消對觀察內(nèi)容的監(jiān)聽。這可以讓 ARC 正常的回收內(nèi)存。

如果沒有DisposeBag,會(huì)有兩種后果:要么Observer會(huì)產(chǎn)生一個(gè) retain cycle,將無限期的進(jìn)行監(jiān)聽;要么Observer被意外釋放,導(dǎo)致程序崩潰。

所以在設(shè)置Observable對象時(shí),將其添加到DisposeBag中。

核心邏輯

可以參考我的博客RXSwift-核心邏輯,RXSwift正式基于這樣的核心邏輯才實(shí)現(xiàn)函數(shù)響應(yīng)式編程。

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容