RAC信號操作解釋合集

我一直覺得,RAC是仿佛已經被遺忘的話題,擅長的人已經把它化為內力,不擅長的早已忘記這個技術的存在,這個暫且按住不談。我們都知道RAC的強大在于它豐富的信號操作,RAC提供了幾十種信號操作,掌握這些信號操作,無疑會大大增強使用RAC的效力。這篇文章其實15年就寫了,一直放在印象筆記里面,自己查閱使用,現在有共享出去的需求,所以拿出來放在這里。

這些內容,都是當時研究RAC的時候,閱讀源碼的時候做的筆記,有許多當時理解錯誤和不足的地方,如果有誤導,非常抱歉;加上當時時間比較倉促,還有其他的任務,很多內容沒有經過校正和確定,所以肯定會有謬誤的地方。僅作參考,如果發現錯誤,歡迎留言,我這邊會予以更改。我也會抽時間進行再次研究和修正。RAC的門檻很高,初學者有諸多障礙,所以業內的關注度有限。不關注不代表不重要,掌握響應式編程的用法,打破自己一貫的編程方式,對于嚴格要求自己的開發者的重要性不言而喻。同時對于工程的實踐意義重大,越復雜的項目,使用RAC越有勢如破竹之感。希望有一天,這個可以成為一個很大眾,被多數人掌握的技術。


scanWithStart

- (instancetype)scanWithStart:(id)startingValue reduceWithIndex:(id(^)(id,id,NSUInteger))reduceBlock

///使用reduceBlock從左到右合并receiver包含的值.這是一個很有趣的操作,暫時沒發現有什么用處。詳細解釋一下:

startingValue是第一個left值,信號的第一個值是right值,使用block進行合并,返回新值,以后的left是上一次合并之后的值,right是next的值。效果的疊加。可以參見文檔對數組的操作示例



scanWithStart

- (instancetype)scanWithStart:(id)startingValue reduce:(id(^)(idrunning,idnext))reduceBlock;

///除了少一個index的參數,其余同上


combinePreviousWithStart

- (instancetype)combinePreviousWithStart:(id)start reduce:(id(^)(idprevious,idnext))reduceBlock

///合并left和right的值,但是不會把合并的結果和下一個值在疊加效果,即每次都是left和right的疊加。實現原理是scanWithStart和map的組合。


ignore

- (instancetype)ignore:(id)value

///忽略信號中的某些值


reduceEach

- (instancetype)reduceEach:(id(^)())reduceBlock

///信號必須發送的是一組值(RACTuple),通過reduceBlock接收一組值,然后處理返回一個值


startWith

- (instancetype)startWith:(id)value

///信號會首先發送一個值,實現原理是concat


skip

- (instancetype)skip:(NSUInteger)skipCount

///跳過前幾次的值,實現原理是bind


take

- (instancetype)take:(NSUInteger)count

///取前幾次的值,實現原理是bind


join

+ (instancetype)join:(id)streams block:(RACStream* (^)(id,id))block

///類方法,私有方法。合并一群信號,方式是left、right形式的,即一一合并,第一個和第二個合并,合并的結果在和第三個合并,而合并是block執行,輸入的參數是left、right,返回一個合并好的stream,直到合并結束,返回一個stream。然后在把這個stream的值拆包(RACTuple)。最終的作用是把幾個stream的值合并到一個數組中輸出。但是是內部方法,不對外提供調用接口,是其他幾個操作的基礎。關鍵在于block的實現。


zip

+ (instancetype)zip:(id)streams

///類方法。它合并一組信號。必須任何一個信號都發送了一次值,合并后的信號才會把這幾個信號的同一批次的值封裝為RACTuple發送出去。例如合并兩個信號,第一個信號發了兩次1,2,第二個沒發送,那么不會有信號發出;如果第二個信號發送一次3,那么合并之后的信號會發送一個RACTuple(1,3),除非第二個信號在發送第二個值,第一個信號發送的第二個值才會被一起包裝作為一次信號發送出去。實現原理是上面的join加上zipWith。


zip

+ (instancetype)zip:(id)streams reduce:(id(^)())reduceBlock

///類方法。根據上面一個方法的解釋。它添加一個功能是,把所有的組合值,經過reduceBlock處理,合并成一個值返回。實現原理是zip加上reduceEach。


concat

+ (instancetype)concat:(id)streams

///類方法。把一組信號串聯起來,前面一個信號complete,后面一個信號才開始發揮作用。


takeUntilBlock

- (instancetype)takeUntilBlock:(BOOL(^)(idx))predicate

///predicate返回yes的時候,停止信號的訂閱。實現原理是bind。


takeWhileBlock

- (instancetype)takeWhileBlock:(BOOL(^)(idx))predicate

///和上面一個方法相反。


skipUntilBlock

skipWhileBlock

distinctUntilChanged

///這兩個方法和上面兩個方法相反,不一一解釋。



doNext

- (RACSignal*)doNext:(void(^)(idx))block;

///用來往信號注入side effects,顯示標記side effects的方法,在每次信號subscribeNext之前執行。詳見https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/BasicOperators.md#transforming-streams

有示例解釋。實現很簡單,訂閱原始信號,返回新的信號,原始信號有數據流動的時候,執行doNext的block,同時新的信號被訂閱,直接向訂閱者發送sendNext消息和數據參數。side effects的


doError

///用來消除side effects的方法,解釋同上原理。


doCompleted

///用來消除side effects的方法,解釋同上原理。


throttle

- (RACSignal*)throttle:(NSTimeInterval)interval;

///每次發送的數據,都經過interval的間隔之后才發出。在interval時間內發送的所有信號只有最后一個數據被發送,前面的都會被拋棄。


throttle

- (RACSignal*)throttle:(NSTimeInterval)interval valuesPassingTest:(BOOL(^)(idnext))predicate;

///每次發送的數據,經過predicate block的過濾,返回NO,代表不需要被過濾,直接發送數據,返回YES,那么需要經過interval的間隔之后才發出。在interval時間內發送的所有信號只有最后一個數據被發送,前面的都會被拋棄。


delay

- (RACSignal*)delay:(NSTimeInterval)interval

///每次發送的next和complete事件,都有interval時間的延遲,但是error一直都會立即發送。


repeat

- (RACSignal*)repeat;

///當信號發送complete的時候,重新訂閱。


initially

- (RACSignal*)initially:(void(^)(void))block;

/// 當每次訂閱產生的時候,會調用initially方法。這個方法的參數block是定義:訂閱side effect的地方。就像doNext一樣,在每次sendNext之前會被調用,用來標記sendNext的side effect效應。這一類的方法統稱為顯式的表達side effect(見side effect分析)


finally

- (RACSignal*)finally:(void(^)(void))block;

/// 當發送completes or errors事件的時候被調用,解釋同上(side effect)


bufferWithTime

- (RACSignal*)bufferWithTime:(NSTimeInterval)interval onScheduler:(RACScheduler*)scheduler;

///把信號的數據緩存起來,每interval秒發送一次,當發送complete的時候,會把當前的buffer內容全部發出去。實現的原理是,內部訂閱原始信號,每次原始信號的值被發送的時候,有一個可變數組把它存起來,并且dispatch_after interval秒之后,執行新的信號的sendNext(發送的是RACTuple對象,包含緩存的數據)


collect

- (RACSignal*)collect;

///這個方法使用和理解起來比較簡單,他把信號所有的數據都存到一個數組里面,等到complete事件的時候,把這個數組發出去。實現原理是:它的實現原理比較復雜,是一連串操作的集合。經過aggregateWithStartFactory操作、defer操作、scanWithStart、bind等等(后續解釋)。總之,它的大致原理是:1、scanWithStart,前面我們解釋過,這個是使用block合并信號的前一個值和下一個值,返回一個新值的操作,初始值我們指定一個可變數組,block的操作是把下一個值添加到這個數組中來;2、takeLast操作來保證原始信號結束之后(complete)發送最近的一個結果。


takeLast

- (RACSignal*)takeLast:(NSUInteger)count;

///當原始信號發送complete之后,發送count數量的最近的next數據。實現原理是:內部訂閱原始信號,新建一個count個數的數組,每一個值都被存儲起來,超出數組數量的時候,移除最初的數據,保證數組數量最多只有count個,當complete的時候,send出去這些數據。


combineLatestWith

- (RACSignal*)combineLatestWith:(RACSignal*)signal;

///每當2個初始的信號發送next事件的時候,返回的信號都會把兩個信號最近的值發送出去。實現原理是:內部訂閱兩個信號,剩下的你懂的。(必須2個信號都最少返回過數據)


combineLatest

+ (RACSignal*)combineLatest:(id)signals;

///合并一批信號,剩余的同上,實現原理是:基于join和combineLatestWith,join前面解釋過,合并一批stream,采用left、right的方式,而left每次是block提供,這里block的實現是基于combineLatestWith的原理合并,join在負責最后的拆包(拆一層層的RACTuple,詳見前文介紹)


combineLatest

+ (RACSignal*)combineLatest:(id)signals reduce:(id(^)())reduceBlock;

///合并一批信號,返回的值會作為參數的形式通過reduceBlock執行,返回一個值,官方有例子介紹。實現原理是:通過combineLastest和reduceEach(前問有解釋),把一組值通過參數block合并為一個值。


merge

- (RACSignal*)merge:(RACSignal*)signal;

///合并兩個信號,這個和combine的區別是:combine合并信號,每次發送的一個RACTuple對象,即包含每個信號的最新的value,而merge合并的信號的意思是,每個原始信號sendNext都會被當作返回信號的一次值發送(基于bind,綁定)。它的實現原理是flatten,首先創建一個信號,信號發送的數據是要被合并的信號(循環),然后flatten操作是攤平這些信號,走到flattenMap操作,它的參數是一個直接返回接收參數的block對象,我們知道flattenMap的內部實現是基于bind,bind內部訂閱原始信號-》信號經過剛才的block處理(直接返回)-》訂閱這個block執行之后返回的信號-》信號的每次發送數據都等于最終返回的信號在發送數據(subscribe sendNext:…)。flattenMap有修改的意味,但是flatten直接返回的是信號本身。


merge

+ (RACSignal*)merge:(id)signals;

///同上


flatten

- (RACSignal*)flatten:(NSUInteger)maxConcurrent;

///這是flatten的另一種實現,順帶有附加效果:最大訂閱數,超過最大訂閱數的信號被隊列管理起來,當訂閱的信號complete的時候,從隊列中補充一個進來。可以參考一下和flatten的實現區別,一個基于flattenMap,只不過是沒有map,另一個是基本的實現,就是原始信號發送的都是信號,直接訂閱這些信號,數據作為返回的信號數據,類似于簡化版的bind。


then

- (RACSignal*)then:(RACSignal* (^)(void))block;

///忽略所有的原始信號值,直到complete,然后訂閱后面的信號。實現原理是:concat(前文有解釋)


concat

- (RACSignal*)concat;

///前文有解釋,這里分析實現原理:它的核心操作就是一句話flatten: 1 ? 我們知道flatten是控制最大訂閱數,一個結束,另一個補充上來


aggregateWithStart

- (RACSignal*)aggregateWithStart:(id)start reduce:(id(^)(idrunning,idnext))reduceBlock;

///這系列的3個方法都見上面collect的方法解釋。


setKeyPath

- (RACDisposable*)setKeyPath:(NSString*)keyPath onObject:(NSObject*)object;

- (RACDisposable*)setKeyPath:(NSString*)keyPath onObject:(NSObject*)object nilValue:(id)nilValue

///把一個信號和一個對象的keyPath關聯起來,每次信號發送數據,會被自動綁定到對象的該屬性上面


interval

+ (RACSignal*)interval:(NSTimeInterval)interval onScheduler:(RACScheduler*)scheduler;

+ (RACSignal*)interval:(NSTimeInterval)interval onScheduler:(RACScheduler*)scheduler withLeeway:(NSTimeInterval)leeway;

///在scheduler上面部署每隔多少秒執行任務。實現原理是基于- (RACDisposable*)after:(NSDate*)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void(^)(void))block;底層原理是GCD技術。


takeUntil

- (RACSignal*)takeUntil:(RACSignal*)signalTrigger;

- (RACSignal*)takeUntilReplacement:(RACSignal*)replacement;

///原始信號一直發送信號,直到,替代的信號發出事件,原始信號終止。


catch

- (RACSignal*)catch:(RACSignal* (^)(NSError*error))catchBlock;

- (RACSignal*)catchTo:(RACSignal*)signal;

///原始信號出現錯誤的時候,使用block返回新的信號替代,block的參數是錯誤信息


try

- (RACSignal*)try:(BOOL(^)(idvalue,NSError**errorPtr))tryBlock;

- (RACSignal*)tryMap:(id(^)(idvalue,NSError**errorPtr))mapBlock;

///原始信號每次發送的信號都會經過tryBlock的檢查,如果返回NO,那么信號會發送error事件。map類似。都是基于flattenMap。


first

- (id)first;

- (id)firstOrDefault:(id)defaultValue;

- (id)firstOrDefault:(id)defaultValue success:(BOOL*)success error:(NSError**)error;

///返回第一個next數據,這個是一個阻塞調用(會阻塞當前線程),使用了NSCondition狀態鎖機制。


waitUntilCompleted

- (BOOL)waitUntilCompleted:(NSError**)error;

///阻塞直到完成(sendComplete)


defer

+ (RACSignal*)defer:(RACSignal* (^)(void))block;

///直到訂閱時候才真正創建一個信號


switchToLatest

- (RACSignal*)switchToLatest;

+ (RACSignal*)switch:(RACSignal*)signal cases:(NSDictionary*)casesdefault:(RACSignal*)defaultSignal;

///原始信號必須是發送信號的信號(sendNext:(RACSignal)…),發送的信號會被訂閱,直到發送下一個信號,前一個被發送的信號就終止訂閱,方法的作用是,每次訂閱最新的信號。和map一起往往被認為是替代flattenMap的方案。


if

+ (RACSignal*)if:(RACSignal*)boolSignal then:(RACSignal*)trueSignalelse:(RACSignal*)falseSignal;

///boolSignal返回bool值,決定當前訂閱trueSignal還是falseSignal。基于switchToLatest和Map


toArray

- (NSArray*)toArray;

///這是一個阻塞操作,complete的時候把所有的信號數據發出去(數組),基于collect和first操作


sequence

@property(nonatomic,strong,readonly)RACSequence*sequence;

///把信號轉換成RACSequence


publish

- (RACMulticastConnection*)publish;

- (RACMulticastConnection*)multicast:(RACSubject*)subject;

- (RACSignal*)replay;

- (RACSignal*)replayLast;

- (RACSignal*)replayLazily;

///多播系列,見side effects講解


timeout

- (RACSignal*)timeout:(NSTimeInterval)interval onScheduler:(RACScheduler*)scheduler;

///設置超時處理,超時會發error事件


deliverOn

- (RACSignal*)deliverOn:(RACScheduler*)scheduler;

///在指定的scheduler分發三種事件,side effect還會在原始的scheduler上


subscribeOn

- (RACSignal*)subscribeOn:(RACScheduler*)scheduler;

///side effect和三種事件都在指定的scheduler上分發,這個操作比較危險,因為side effect可能不是線程安全的。


deliverOnMainThread

///在主線程上分發事件


groupBy

- (RACSignal*)groupBy:(id (^)(idobject))keyBlock transform:(id(^)(idobject))transformBlock;

- (RACSignal *)groupBy:(id (^)(idobject))keyBlock;

///把信號分組


any

- (RACSignal*)any;

- (RACSignal*)any:(BOOL(^)(idobject))predicateBlock;

///原始信號發送next,返回的信號會發送[NSNumber numberWithBool:YES],然后終止訂閱


all

- (RACSignal*)all:(BOOL(^)(idobject))predicateBlock;

///原始信號發送所有的next通過predicateBlock驗證,才會返回YES,否者為NO


retry

- (RACSignal*)retry;

- (RACSignal*)retry:(NSInteger)retryCount;

///發送error事件之后重試,重新訂閱


sample

- (RACSignal*)sample:(RACSignal*)sampler;

///sampler每次發送信號的時候,會把原始信號的最新值發出去


ignoreValues

///忽略所有的next值,只接收complete和error事件


materialize

///把每次信號的值封裝成RACEvent對象


dematerialize

///跟上面相反,解封


not

///原始信號必須發送NSNumber,not操作會返回轉化后的bool值的相反值


and

///原始信號必須發送RACTuple,包含的必須是NSNumber對象,所有的對象轉換后的bool值必須是YES,才會返回YES


or

///原始信號必須發送RACTuple,包含的必須是NSNumber對象,只要有對象轉換后的bool值是YES就會返回YES


reduceApply

///原始信號發送RACTuple,包含最少2個要素,第一個是block對象,第二個是參數,然后執行。。。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內容