Reactive Cocoa Tutorial [2] = "百變RACStream";

Reactive Cocoa Tutorial 系列,轉載請註明該文源地址 -- by sunnyhtml


  · Overview

  在RAC下開發乾的最多的事就是創建RACSignal和subscribe RACSignal了,它是RAC的核心所在。本篇介紹了RAC的運做原理和設計思路,從函數式編程造成的RACStream繼而介紹它的子類 - RAC最核心的部分RACSignal。編程

· 函數式編程

  咱們知道Reactive Cocoa是函數式編程(Functional Programing)(FP)思想的實現。FP有一套成熟的理論,這裏只講講我我的理解吧。數組

  我以爲FP就是「像計算函數表達式同樣來解決一個問題」,舉個栗子,中學題:函數式編程

已知:f(x) = 2sin(x + π/2), 求 f(π/2)的值。

  其中x是這個函數的輸入,f(x)爲計算的輸出結果,求f(π/2)時給定了x天然能計算出個結果來(說實話我真忘了咋算了)函數

固然,仔細看這個函數,實際上是能夠分解成幾個小函數的:spa

f1(x) = x + π/2
f2(x) = sin(x)
f3(x) = 2x

  而原來的f(x)能夠被小函數組合:設計

f(x) = f3(f2(f1(x)))

  因此不可貴出這麼個推論:要是我手上有足夠的基本函數,我就能用上面的組合的方法組合出任意一個複雜的函數了。再想一想事實上這些年來學數學的過程不就是在一個個積累基本函數的過程嘛,從基本運算,到三角函數,到乘方開方,再到微積分。基本函數愈來愈多,能解決的數學問題也愈來愈複雜。code

  再來看一個函數是怎麼構成的,FP理論裏叫monads,十分抽象,沒讀懂,但能理解出來:一個函數只要有一個對於輸入值的運算方法一個返回值,就夠了。也容易理解,給它一個輸入,乾點事情,給出一個輸出,就好了,固然現實狀況要複雜得多(好比說輸出值自己就是個函數?)有些函數是有輸入的條件的,好比原來數學解個函數時候常常跟個做用域或者限制條件,好比f(x) = 10 / x , (x不爲0),要是傳個0這個函數就認爲計算錯誤。htm

  對於像上面栗子的函數,每一個函數都能接收上一個函數輸出的結果,做爲本身的輸入,這樣才能嵌套生成最終結果,同時,計算的順序也是必定從裏向外,因此換個寫法能夠寫成:對象

start ---x--> f1(x) --(temp value1)--> f2(temp value1) --(temp value2)--> f3(temp value2) ---> result

  因而乎嵌套就被表示成了序列,來個高大上的名字怎麼樣,就叫(Stream)

·RACStream

  這就是RACStream所表示的含義。

  按照上面說的,其實RACStream的名字有點點歧義,對於一個RACStream對象,它在乎義上等同於上面的f1(x),f2(x),f3(x),而不是那一大串總體,表示總體的應該是最外層的和f(x)對應的那個對象,叫個RACStreamComponent比較好?理解時候得注意下。

  因此做爲一個基本函數的RACStream應該至少應該有:

    1. 怎麼傳入值
    2. 怎麼返回值
    3. 怎麼與其餘函數組合
    4. 怎麼實現函數的做用域(監測輸入值來作處理)
    5. 這函數叫啥- -

  得益於在Objc下實現,因此輸入輸出的「值」都用個id類型就好了,遇到多個值的組合就用RACTurple(能夠把多個值壓包和解包,類比WINRAR),1和2解決

  RACStream從實例變量來看只有一個name,固然它也只應該有個name - -,5解決

  裏面重點問題就是上面的3和4了。因爲函數組合以後仍然是個函數,因此也很容易理解兩個Stream對象的組合其實就是生成一個新的Stream對象,它返回了分別由兩個子Stream前後運算產生的最終結果

   觀摩一下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做爲一個描述抽象的父類,這幾個基本方法並無實現,是由具體子類來實現,RACStream的兩個子類分別是RACSignal和RACSequence

  +empty 是一個不返回值,馬上結束(Completed)的函數,意思是執行它以後除了馬上結束啥都不會發生,能夠理解爲RAC裏面的nil。

  +return: 是一個直接返回給定值,而後馬上結束的函數,好比 f(x) = 213

  -bind:是一個很是重要的函數,在Rac Doc中被描述爲‘basic primitives, particularly’,它是RACStream監測「值」和控制「運行狀態」的基本方法,我的認爲看註釋文檔不能理解它是幹嗎的,並且bind英語「捆綁,綁定,強迫,約束」這幾個意思也感受對不上,我以爲叫「綁架」卻是更貼切一點。在-bind:以後,以前的RACStream就處於被「綁架」的狀態,被綁架的RACStream每產生一個值,都要通過「綁架者」來決定:

1. 是否使這個RACStream結束(被綁架者是否還能繼續活着)

2. 用什麼新的RACStream來替換被綁架的RACStream,傳出的結果也成了新RACStream產生的值(綁匪能夠選擇再抓一我的質放以前那個前面)

   舉個具體栗子,RACStream的 - take:方法,這個方法使一個RACStream只取前N次的值(有縮減):

- (instancetype)take:(NSUInteger)count {
    Class class = self.class;
    
    return [[self bind:^{ // self被綁架
        __block NSUInteger taken = 0;

        return ^ id (id value, BOOL *stop) { // 這個block在被綁架的self每輸出一個值得時候觸發
            RACStream *result = class.empty;

            if (taken < count) result = [class return:value]; // 未達到N次時將原值原本來本的傳遞出去
            if (++taken >= count) *stop = YES; // 達到第N次值後幹掉了被綁架的self

            return result; // 將被綁架的self替換爲result
        };
    }]];
}

   -concat: 和 -zipWith: 就是將兩個RACStream鏈接起來的基本方法了:

  1. [A concat:B]中A和B像皇上和太子的關係,A是皇上,B是太子。皇上健在的時候統治天下發號施令(value),太子就候着,不發號施令(value),當皇上掛了(completed),太子登基當皇上,此時發出的號令(value)是太子的。
  2. [C zipWith:D]能夠比喻成一對平等恩愛的夫妻,兩我的是「綁在一塊兒「的關係來組成一個家庭,決定一件事(value)時必須兩我的都提出意見(當且僅當C和D同時都產生了值的時候,一個value才被輸出,CD只有其中一個有值時會掛起等待另外一個的值,因此輸出都是一對值(RACTuple)),當夫妻只要一我的先掛了(completed)這個家庭(組合起來的RACStream)就宣佈解散(也就是沒法湊成一對輸出時就終止)

· 而後呢?

  除了上面幾個基本方法,RACStream還有很多的Operation方法,這些操做方法的實現大都是組合基本的方法來達到特定的目的,雖然是RACStream這個基類實現的,但我以爲仍是放在後面介紹RACSignal的時候做爲它的使用方法來講比較合適,畢竟絕大多數編程的對象的都是RACStream的兩個子類,後面再展開介紹好了。

相關文章
相關標籤/搜索