Reactive Cocoa Tutorial 系列,轉(zhuǎn)載請(qǐng)注明該文源地址 -- by sunny
在RAC下開發(fā)干的最多的事就是建立RACSignal和subscribe RACSignal了,它是RAC的核心所在。本篇介紹了RAC的運(yùn)作原理和設(shè)計(jì)思路,從函數(shù)式編程形成的RACStream繼而介紹它的子類 - RAC最核心的部分RACSignal。
我們知道Reactive Cocoa是函數(shù)式編程(Functional PRograming)(FP)思想的實(shí)現(xiàn)。FP有一套成熟的理論,這里只講講我個(gè)人理解吧。
我覺得FP就是“像計(jì)算函數(shù)表達(dá)式一樣來解決一個(gè)問題”,舉個(gè)栗子,中學(xué)題:
已知:f(x) = 2sin(x + π/2), 求 f(π/2)的值。
其中x是這個(gè)函數(shù)的輸入,f(x)為計(jì)算的輸出結(jié)果,求f(π/2)時(shí)給定了x自然能計(jì)算出個(gè)結(jié)果來(說實(shí)話我真忘了咋算了)
當(dāng)然,仔細(xì)看這個(gè)函數(shù),其實(shí)是可以分解成幾個(gè)小函數(shù)的:
f1(x) = x + π/2f2(x) = sin(x)f3(x) = 2x
而原來的f(x)可以被小函數(shù)組合:
f(x) = f3(f2(f1(x)))
所以不難得出這么個(gè)推論:要是我手上有足夠的基本函數(shù),我就能用上面的組合的方法組合出任意一個(gè)復(fù)雜的函數(shù)了。再想想事實(shí)上這些年來學(xué)數(shù)學(xué)的過程不就是在一個(gè)個(gè)積累基本函數(shù)的過程嘛,從基本運(yùn)算,到三角函數(shù),到乘方開方,再到微積分。基本函數(shù)越來越多,能解決的數(shù)學(xué)問題也越來越復(fù)雜。
再來看一個(gè)函數(shù)是怎么構(gòu)成的,F(xiàn)P理論里叫monads,十分抽象,沒讀懂,但能理解出來:一個(gè)函數(shù)只要有一個(gè)對(duì)于輸入值的運(yùn)算方法和一個(gè)返回值,就夠了。也容易理解,給它一個(gè)輸入,干點(diǎn)事情,給出一個(gè)輸出,就行了,當(dāng)然現(xiàn)實(shí)情況要復(fù)雜得多(比如說輸出值本身就是個(gè)函數(shù)?)有些函數(shù)是有輸入的條件的,比如原來數(shù)學(xué)解個(gè)函數(shù)時(shí)候經(jīng)常跟個(gè)作用域或者限制條件,比如f(x) = 10 / x , (x不為0),要是傳個(gè)0這個(gè)函數(shù)就認(rèn)為計(jì)算錯(cuò)誤。
對(duì)于像上面栗子的函數(shù),每個(gè)函數(shù)都能接收上一個(gè)函數(shù)輸出的結(jié)果,作為自己的輸入,這樣才能嵌套生成最終結(jié)果,同時(shí),計(jì)算的順序也是一定從里向外,所以換個(gè)寫法可以寫成:
start ---x--> f1(x) --(temp value1)--> f2(temp value1) --(temp value2)--> f3(temp value2) ---> result
于是乎嵌套就被表示成了序列,來個(gè)高大上的名字怎么樣,就叫流(Stream)
這就是RACStream所表示的含義。
按照上面說的,其實(shí)RACStream的名字有點(diǎn)點(diǎn)歧義,對(duì)于一個(gè)RACStream對(duì)象,它在意義上等同于上面的f1(x),f2(x),f3(x),而不是那一大串整體,表示整體的應(yīng)該是最外層的和f(x)對(duì)應(yīng)的那個(gè)對(duì)象,叫個(gè)RACStreamComponent比較好?理解時(shí)候得注意下。
所以作為一個(gè)基本函數(shù)的RACStream應(yīng)該至少應(yīng)該有:
得益于在Objc下實(shí)現(xiàn),所以輸入輸出的“值”都用個(gè)id類型就行了,遇到多個(gè)值的組合就用RACTurple(可以把多個(gè)值壓包和解包,類比WINRAR),1和2解決
RACStream從實(shí)例變量來看只有一個(gè)name,當(dāng)然它也只應(yīng)該有個(gè)name - -,5解決
里面重點(diǎn)問題就是上面的3和4了。由于函數(shù)組合之后仍然是個(gè)函數(shù),所以也很容易理解兩個(gè)Stream對(duì)象的組合其實(shí)就是生成一個(gè)新的Stream對(duì)象,它返回了分別由兩個(gè)子Stream先后運(yùn)算產(chǎn)生的最終結(jié)果
觀摩一下RACStream定義的基本方法:
+ (instancetype)empty;+ (instancetype)return:(id)value;- (instancetype)bind:(RACStreamBindBlock (^)(void))block; // for 4- (instancetype)concat:(RACStream *)stream; // for 3- (instancetype)zipWith:(RACStream *)stream; // for 3
RACStream作為一個(gè)描述抽象的父類,這幾個(gè)基本方法并沒有實(shí)現(xiàn),是由具體子類來實(shí)現(xiàn),RACStream的兩個(gè)子類分別是RACSignal和RACSequence
+empty 是一個(gè)不返回值,立刻結(jié)束(Completed)的函數(shù),意思是執(zhí)行它之后除了立刻結(jié)束啥都不會(huì)發(fā)生,可以理解為RAC里面的nil。
+return: 是一個(gè)直接返回給定值,然后立刻結(jié)束的函數(shù),比如 f(x) = 213
-bind:是一個(gè)非常重要的函數(shù),在Rac Doc中被描述為‘basic primitives, particularly’,它是RACStream監(jiān)測“值”和控制“運(yùn)行狀態(tài)”的基本方法,個(gè)人認(rèn)為看注釋文檔不能理解它是干嘛的,而且bind英語“捆綁,綁定,強(qiáng)迫,約束”這幾個(gè)意思也感覺對(duì)不上,我覺得叫“綁架”倒是更貼切一點(diǎn)。在-bind:之后,之前的RACStream就處于被“綁架”的狀態(tài),被綁架的RACStream每產(chǎn)生一個(gè)值,都要經(jīng)過“綁架者”來決定:
1. 是否使這個(gè)RACStream結(jié)束(被綁架者是否還能繼續(xù)活著)
2. 用什么新的RACStream來替換被綁架的RACStream,傳出的結(jié)果也成了新RACStream產(chǎn)生的值(綁匪可以選擇再抓一個(gè)人質(zhì)放之前那個(gè)前面)
舉個(gè)具體栗子,RACStream的 - take:方法,這個(gè)方法使一個(gè)RACStream只取前N次的值(有縮減):
- (instancetype)take:(NSUInteger)count { Class class = self.class; return [[self bind:^{ // self被綁架 __block NSUInteger taken = 0; return ^ id (id value, BOOL *stop) { // 這個(gè)block在被綁架的self每輸出一個(gè)值得時(shí)候觸發(fā) RACStream *result = class.empty; if (taken < count) result = [class return:value]; // 未達(dá)到N次時(shí)將原值原原本本的傳遞出去 if (++taken >= count) *stop = YES; // 達(dá)到第N次值后干掉了被綁架的self return result; // 將被綁架的self替換為result }; }]];}
-concat: 和 -zipWith: 就是將兩個(gè)RACStream連接起來的基本方法了:
除了上面幾個(gè)基本方法,RACStream還有不少的Operation方法,這些操作方法的實(shí)現(xiàn)大都是組合基本的方法來達(dá)到特定的目的,雖然是RACStream這個(gè)基類實(shí)現(xiàn)的,但我覺得還是放在后面介紹RACSignal的時(shí)候作為它的使用方法來說比較合適,畢竟絕大多數(shù)編程的對(duì)象的都是RACStream的兩個(gè)子類,后面再展開介紹好了。
|
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注