ReactiveCocoa 小總結

Jerry4me發表於2017-03-18

我的Github地址 : Jerry4me, 本文章的demo連結 : JRReactiveCocoa

RAC與MVVM如今已經不是一個新鮮的玩意了, 對於介紹他們兩的精品文章更是大把, 這篇文章主要是用來記錄自己學習RAC的過程以及RAC的一些用法, 以防以後要用到的時候卻記不起來了.

具體RAC的用法以及本文出現的程式碼均能在我的 Github上, 另外附有2個MVVM的小demo. 歡迎大家檢視, 賞臉的給個star~


RAC程式設計思想

程式設計學的是思想, 學一樣東西最主要是學會它的思想, 那才是它的靈魂, 而不是學習呼叫方法而已.

RAC又被稱為FRP, 函式響應式程式設計.

何為函式式? 把操作寫成一系列巢狀的函式或者方法呼叫

何為響應式? 不需要考慮呼叫順序, 只考慮結果. 一個屬性, 一個請求改變馬上引發一系列改變.

所以RAC即糅合了函式式和響應式程式設計的優點, 使用RAC程式設計不需要考慮程式碼呼叫順序, 只需要考慮結果. 把每一個操作都寫成一系列的巢狀的方法, 使程式碼變得高內聚, 低耦合.


RAC使用場景

資料隨著時間而產生, 例如以下三點 :

  1. UI操作, 連續的動作和動畫部分, 例如某些控制元件跟隨滾動
  2. 網路庫, 因為資料是在一定時間後才返回回來, 不是立刻返回的
  3. 重新整理的業務邏輯, 當觸發點是多種的時候, 業務往往會變得很複雜, 用delegate, notification, observe混用, 難以統一. 這時用RAC能保證上層的高度一致性, 從而簡化邏輯上分層.

RAC類關係圖

RAC類的關係圖如下, 下面會抽出一部分類進行講解, 另外有部分類與用法會在github上的demo上看得到, 還有部分類將不在本文中出現, 本文(demo)只說明瞭一些常用的類與方法.

111862021-24909c97608f0849
ReactiveCocoa類圖.png

訊號源

121862021-d93ae8886277bf50
RACStream.ng

RACSignal

RACSignal只會向訂閱者傳送三種事件 : next, errorcompleted.

RACSignal的一系列功能是通過類簇來實現的. 如 :

核心方法 : -subscribe:.

RACSubject

繼承自RACSignal, 是可以手動控制的訊號, 相當於RACSignal的可變版本.

能作為訊號源被訂閱者訂閱, 又能作為訂閱者訂閱其他訊號源(實現了RACSubscriber協議).

RACSubject有三個用來實現不同功能的子類 :


RACSequence

代表的是一個不可變的值的序列. 不能被訂閱者訂閱, 但是能與RACSignal之間非常方便地進行轉換.

RACSequence由兩部分組成 : headtail, head是序列中的第一個物件, tail則是其餘的全部物件.

RACSequence存在的最大意義就是簡化OC中的集合操作. 並且RACSequence所包含的值預設是懶計算的, 所以不知不覺中提高了我們應用的效能.

push-driven與pull-driven

  • RACSignal : push-driven, 生產一個吃一個, 類似於工廠的主動生產模式, 生產出產品就push給供銷商.
  • RACSequence : pull-driven, 吃一個生產一個, 類似於工廠的被動生產模式, 供銷商過來pull的時候才現做產品.

對於RACSignal的push-driven模式來說, 沒有供銷商(subscriber)籤合同要產品, 當然就不生產了. 只有一個以上準備收貨的供銷商時, 工廠才開始生產. 這就是RACSignal的休眠(cold)和啟用(hot)狀態, 也就是冷訊號熱訊號. 一般情況下RACSignal建立以後都處於cold狀態, 當有人去subscribe才變成hot狀態.

冷訊號與熱訊號

熱訊號 : 主動, 即使你沒有訂閱事件, 仍然會時刻推送. 熱訊號可以有多個訂閱者, 是一對多的關係, 訊號可以與訂閱者共享資訊.

冷訊號 : 被動, 只有當你訂閱的時候, 它才會釋出訊息. 冷訊號只能一對一, 當有不同的訂閱者, 訊息是重新完整傳送的.

ps : 任何的訊號轉換即是對原有訊號進行訂閱從而產生新的訊號. (例如 : Map, FlattenMap等等)

如何區分熱訊號和冷訊號

Subject類似於直播, 錯過了就不再處理, 而Signal類似於點播, 每次訂閱都從頭開始重新傳送.

我們能得出 :

將冷訊號轉化成熱訊號

RAC幫我們封裝了一套可以輕鬆將冷訊號轉換成熱訊號的API :

其中最重要的就是- (RACMulticastConnection *)multicast:(RACSubject *)subject;, 其他幾個方法都是間接呼叫它的.

本質 : 使用一個Subject來訂閱原始訊號, 並讓其他訂閱者訂閱這個Subject, 由於RACSubject本身為熱訊號, 所以源訊號此時就像由冷訊號變成了熱訊號.


訂閱者

RACSubscriber

其中 -sendNext:, -sendError:-sendCompleted 分別用來從RACSignal接收 next, errorcompleted 事件, 而-didSubscribeWithDisposable:則用來接收代表某次訂閱的disposable物件.

一個RACDisposable物件就代表這一次訂閱, 並且我們可以用它來取消這次訂閱.

RACSubscriber就是真正的訂閱者, 而RACPassthroughSubscriber可以使得一個訂閱者可以訂閱多個訊號源, 即擁有多個RACDisposable物件, 並能隨時取消其中的任何一次訂閱. 為了實現這個功能, RAC就引入了RACPassthroughSubscriber類, 它是RACSubscriber類的一個裝飾器, 封裝了一個真正的訂閱者 RACSubscriber 物件, 它負責轉發所有事件給這個真正的訂閱者, 而當此次訂閱被取消時, 它就會停止轉發

RACMulticastConnection

131862021-8a006ce730feb460
RACMulticastConnection.png

使得不管外面有多少個訂閱者, 對源訊號的訂閱只會有一次. 為了防止副作用的產生, 使用的便是multicast機制

multicast的機制

機制一 : 能防止某訊號被多次訂閱時呼叫多次didSubscribe block產生副作用.

機制二 : 實現replay, 即每當有訂閱者訂閱時, 會將之前快取中的sendNext重新傳送給該訂閱者.

副作用

  • 函式的處理過程中, 修改了外部的變數(例如 : 全域性變數, 成員變數等)
  • 函式的處理過程中, 出發了一些額外的動作(例如 : 傳送了一個全域性的Notification, 在console列印了一行資訊, 儲存了檔案, 觸發了網路, 更新了螢幕等)
  • 函式的處理過程中, 受到外部變數的影響(例如 : 全域性變數, 成員變數等, block中捕獲到的外部變數也算)
  • 函式的處理過程中, 受到執行緒鎖的影響

以上都算副作用. 然而冷訊號有可能因為有多個訂閱者訂閱而產生極大的副作用, 例如傳送了同一個網路請求若干次, 同一個計算做了若干次等等, 這些問題都可以通過把這個冷訊號轉化成熱訊號得以解決.


排程器

RACScheduler

RAC中對GCD的簡單封裝. 子類如下 :


清潔工

RACDisposable

在訂閱者訂閱訊號源的過程中, 可能會產生副作用或者消耗一定的資源, 所以在取消訂閱或完成訂閱的時候我們就需要做一些資源回收和辣雞清理的工作. 核心方法為-dispose

總的來說就是在適當的時機呼叫disposable物件的-dispose方法而已.


RAC常見巨集

用法在demo中


RAC中潛在的記憶體洩漏及解決方法

RACObserve

如果在block中使用到了RACObserve, 則必須加上@weakify@strongify, 儘管沒有顯示使用到了self. 文件事例如下 :

RACSubject

RACSubject例項進行map操作之後, 傳送完畢一定要呼叫-sendCompleted, 否則會出現記憶體洩漏; 而RACSignal例項不管是否進行map操作, 不管是否呼叫-sendCompleted, 都不會出現記憶體洩漏.

原因 : 因為RACSubject是熱訊號, 為了保證未來有事件發生的時候, 訂閱者可以收到資訊, 所以需要對持有訂閱者!

ps : 幾乎所有操作底層都會呼叫bind這樣一個方法, 包括但不限於以下方法 : map, filter, merge, combineLatest, flattenMap

所以 : 對訊號操作完成記得傳送-sendCompleted. (或者-sendError).

執行緒安全

Signal events是線性的, 不會出現併發的情況, 除非顯示地指定Scheduler. 所以-subscribeNext:裡的block不需要加鎖, 其他的events會依次排隊, 直到block處理完成.

為了方便除錯, 最好給訊號指定Name : -setNameWithFormat:


參考文章 :

ReactiveCocoa 和 MVVM 入門

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

ReactiveCocoa 小總結 ReactiveCocoa 小總結