圖解 RACCommand 底層實現原理

uouopp 9年前發布 | 11K 次閱讀 iOS開發 移動開發

RACCommand作為RAC框架里比較特殊的存在,繼承于NSObject,它是一個類而不是信號。整個類的結構也比較簡潔,根據官方的解釋,可以使用它來創建和訂閱相應事件的信號。對于副作用相關的操作,可以帶來極大的方便。RACCommand類本身主要可以分為5個屬性3個方法。

初始化

接下來對于圖中羅列的方法和屬性做一個分析。

- (id)initWithSignalBlock:(RACSignal * (^)(id input))signalBlock;
- - (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock;

這兩個方法可以放在一起分析, initWithSignalBlock 其實就是調用 initWithEnabled ,傳入的enableSignal為空,即默認信號可執行。在源碼里有說明

if (enabledSignal == nil) {  
    enabledSignal = [RACSignal return:@YES];
} else {
    enabledSignal = [[[enabledSignal
                       startWith:@YES]
                       takeUntil:self.rac_willDeallocSignal]
                       replayLast];
}

RACCommand的初始化,實現代碼很長,通常來說,分成4部分來分析。為了更清晰地理解,參照著源碼和其他分析文章,畫了這張圖

圖中紅色箭頭標注了初始化的順序。

對于executionSignals進行初始化。

_activeExecutionSignals 是一個數組,在初始化方法里給他重新定義為可變數組。當數組里有新的信號加入的時候,會將新信號包裝成元祖。通過一些處理后得到二階熱信號 newActiveExecutionSignals ,因為是二階,所以里面還包含著其他信號。在第一步里,對 newActiveExecutionSignals 進行了過濾,只留下非錯誤信號,即得到RACCommand的屬性之一 executionSignals ,表示包含信號的信號。

對于errors進行初始化

對第一步里生成的 newActiveExecutionSignals ,進行另一種操作,先用flattenMap操作,將其降為一階信號,然后轉化為RACCommand的屬性之一 errors 。

RACCommand會將錯誤信號都裝到它自己的errors信號中,看到這單詞復數形式,應該就能想到里面可以包含很多錯誤。

對于executing進行初始化

屬性 executing 表示是否有信號正在執行。對于一開始的 _activeExecutionSignals ,使用宏RACObserve進行監聽,一旦出現變化,那么會生成一個包裝BOOL的信號 immediateExecuting ,它的初始值被設置為NO,一旦發生了改變,就會發送信號。取其最新值給 executing ,這即是屬性 executing 的初始化過程。

對于enabled進行初始化

監聽 allowsConcurrentExecution ,默認值是NO,如果變化了,返回一個包裝了YES的信號給 moreExecutionAllowed 。如果沒有變化,判斷immediateExecuting,對它進行取非運算,得到的BOOL包裝成信號給 moreExecutionAllowed 。 moreExecutionAllowed 表示是否執行下一個信號,這里將它和enableSignal進行了如圖中的運算。enableSignal是方法初始化時傳入的參數,它的具體內容就是前面提到的這段代碼

if (enabledSignal == nil) {  
    enabledSignal = [RACSignal return:@YES];
} else {
    enabledSignal = [[[enabledSignal
                       startWith:@YES]
                       takeUntil:self.rac_willDeallocSignal]
                       replayLast];
}

當enableSignal為nil的時候,就會傳YES給它。如果不是nil,也給它一個初始值YES,防止空信號比如[RACSignal empty],[RACSignal never]。

二者運算之后得到了immediateEnabled,從單詞字面理解,算是一個短暫的變量,連接這些信號,當變化時取最新值發送,來得到最后的 enabled 屬性。

總結來說

RACCommand初始化,就是對它的各項屬性進行了初始化。signal在初始化方法里只是通過Block傳入,并沒有做其他操作。傳入的信號,它的作用體現在接下來介紹的 execute 方法里。

execute

根據execute的實現源碼,在一開始的時候會先判斷是否可執行,如果不能,則直接返回錯誤信號。如果可以執行,那么開始執行這一系列動作。從SginalBlock里獲得信號,將其轉換為執行信號,這時候的信號是一個冷信號,通過malticast操作,將其轉換為熱信號,添加到activeExecutionSignals數組中。然后實現信號的訂閱,最后返回,完成整個RACCommand的執行過程。

rac_command

RACCommand一個常見的應用是將按鈕點擊事件與RACCommand綁定。在不看源碼的情況下,從外部理解,應該是類似于以下的邏輯

[button addTarget:id action:@selector(click) forControlEvents:UIControlEventTouchUpInside];

- (void)click{
  [Command execute:id]
}

RAC框架里給UIButton添加了 rac_command 的屬性。實現的代碼比較簡潔,涉及了runtime函數里的對象關聯,下面依然通過一張圖來解釋它實現的過程。

關聯的目的在于使得UIButton的target = self,action = @selector(rac_commandPerformAction:)

參考

主要是參考了 ReactiveCocoa 中 RACCommand底層實現分析 來幫助理清關系,畫出以上關系圖。

 

來自:http://www.jianshu.com/p/c7d7169e462a

 

 本文由用戶 uouopp 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!