Reactive Cocoa

Functional Reactive Programming

Functional Reactive Programming(函數響應式(反應式)編程,如下簡稱FRP)是一種響應變化的編程範式,它能經過一種信號機制來記錄值的變化,信號能夠被疊加、分割或合併,經過對信號的組合,就能夠對值進行監聽,有點像數學中的各類公式。例如:git

f(x) = x^2 + 2x + 1;github

每當x變化時,f(x)的值也隨之變化,能夠看出,這個函數是由一個基本函數構成的,即:編程

f(x) = f1(x)^2;
f1(x) = x + 1;數組

數學中有個結論,大體的意思就是大部分的函數,不管多複雜,都能由可列個基本函數構成。簡單說來,計算f(x)的過程就是先計算f1(x)的值,再帶入f(x)中計算,這個計算過程就是FRP中的核心之一,事件流(Stream)。每當x發生變化時,f(x)的值理應響應這種變化,這是FRP的另一個核心:屬性(Properties)。異步

ReactiveCocoa

ReactiveCocoa是Github的一個開源的項目,是IOS平臺上對FRP的實現。函數

RACStream

RACStream就對應FRP中的事件流,一個RACStream對象的意義至關於上面的f1(x)(每個基本函數都有一個與之對應的RACStream對象),最後組合出來的函數f(x)則由RACStreamComponent表示。一個RACStream對象應有幾個基本要素:spa

  1. 傳入值
  2. 返回值
  3. 如何與其餘函數組合
  4. 如何肯定函數的做用域
  5. 函數名
在Objective C中,全部的對象均可以用id表示,多個值的組合能夠用RACTurple(能夠吧多個值壓包和解包),一、2搞定;
 
函數名就叫RACStream對象的name就行,5解決;
 
因爲兩個函數組合以後仍是一個函數,故兩個RACStream對象組合後是一個新生成的Stream對象,它的輸入是第一個Stream的輸入值(或兩個Stream的輸入),返回值是第二個Stream運算後獲得的值(或兩個Stream的輸出),RACStream中有兩個函數,concat:和zipWith:可以將兩個Stream對象組合起來,concat是將兩個Stream串行鏈接起來,第二個Stream的輸入值爲第一個Stream的輸出值(也就是說,只有當第一個Stream執行完才能執行第二個Stream)。zipWith是將兩個Stream組合起來,當兩個Stream都產生輸出值時,組合成的Stream才輸出值(RACTuple),其中任何一個Stream沒法輸出,組合成的Stream都沒法輸出,3解決;
 
RACStream中有一個bind:方法,是用來監測值和控制運行狀態的。
 

RACSignal

信號,ReactiveCocoa中的核心,是一個主動信號流,它表示將來將要發送的數據,也能夠將它當作一個「事件轉發器」。一個signal和一個數據源綁定,當數據源的數據有更新時,signal會向signal全部的subscriber(訂閱者)發送事件。線程

Signal只會向subscriber發送三種類型的事件:code

    • Next:接受下一個事件
    • Error:事件發生錯誤,沒法繼續接受
    • Completed:完成接受,(textField中的值不會再改變或是用戶不但願繼續接受新的值)

一個Signal的生命週期內能夠接受任意多個Next事件,一個Error事件或者是Completed事件(兩種事件只可能出現一種)。對象

 

最有意思的是,Signals之間可以進行各類組合,具體說來,它能夠被修改(Map)、過濾(Filter)、疊加(Combine)和串聯(Chain)。

Map

Filter

1 [[self.textField.rac_textSignal filter:^BOOL(NSString*value) {    
2     return [value length]>= 3; 3 }] subscribeNext:^(NSString*value) { 4 NSLog(@"Text field has been updated: %@", value); 5 >}]; 

textField的text改變時發出的signal會先被進行過濾,只有當text的長度大於等於3時,纔會被髮到下一個接收方繼續執行。

Combine

1 RAC(self,login.enabled) =
2         [RACSignal combineLatest:@[_userName.rac_textSignal,
3                                    _password.rac_textSignal]
4                           reduce:^(NSString *userName, NSString *password){
5                                return @(userName.length > 0 && password.length > 0);
6                            }];

 

userName和password的text改變的signal被聯合在了一塊兒,只有當兩個text的長度都大於0時,login按鈕的enable才爲YES。 

 

RACSubscriber

subscriber(訂閱者),接受signal發出的事件(next,complete,error)

@weakify(self);
    RACSignal *aSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber){
        @strongify(self);
        [self doSomethingWithSuccessHandler:^{
            [subscriber sendNext:nil];
        }];
        return nil;
    }];
 
[aSignal subscribeNext:^(id x){
        NSLog(@"next event sended");
    }];

  

 

@weakify和@strongify的成對使用是確保在block中引用self不會引發循環引用的問題,這是RAC中定義的宏。

creatSignal函數建立了一個Signal,並定義了每次signal被訂閱時須要作的事:doSomethingWithSuccessHandler:,這是一個異步的操做,當操做成功完成時,執行[subscriber sendNext:];告訴每個訂閱者,執行下一步操做。

注意,若是沒有下面一句[aSignal subscribeNext:],則aSignal只會被建立,可是不執行block中的內容(doSomethingWithSuccessHandler:),這時signal沒有一個訂閱者,通常稱之爲冷信號(Cold)。而只有當一個Signal被訂閱了之後([aSignal subscribeNext:]),纔會執行block中的內容,這時signal有了一個訂閱者,它會變成熱信號(Hot),會執行creatSignal:後的block。當block中的內容執行到[subsriber sendNext:]時,全部的subscriber就會執行下一步的操做([aSignal subscribeNext:]後的block)。

反作用,一個信號(RACSignal)每添加一個訂閱者(有一個對象執行[aSignal subscribeNext:]),creatSignal後的block就會被執行一次,這或許是你想要的,或許不是,能夠寫成[[RACSignal creatSignal:...] replay];避免屢次執行creatSignal的block

 

RACScheduler

RACScheduler是ReactiveCocoa中對線程的簡單封裝,事件能夠在指定的scheduler上分發執行,默認狀況是事件都在一個默認的後臺線程裏面執行,如遇特殊狀況須要在主線程調用,使用 deliverOn:可切換線程。

 

 

 

(未完待續...)

相關文章
相關標籤/搜索