[iOS] 響應式編程開發-ReactiveCocoa(一)

什麼是響應式編程

  響應式編程是一種面向數據流和變化傳播的編程範式。這意味着能夠在編程語言中很方便地表達靜態或動態的數據流,而相關的計算模型會自動將變化的值經過數據流進行傳播。編程

  例如,在命令式編程環境中,a:=b+c表示將表達式的結果賦給a,而以後改變b或c的值不會影響a。但在響應式編程中,a的值會隨着b或c的更新而更新。
電子表格程序就是響應式編程的一個例子。單元格能夠包含字面值或相似"=B1+C1"的公式,而包含公式的單元格的值會依據其餘單元格的值的變化而變化。網絡

  響應式編程最初是爲了簡化交互式用戶界面的建立和實時系統動畫的繪製而提出來的一種方法,但它本質上是一種通用的編程範式。併發

什麼是ReactiveCocoa

  ReactiveCocoa (RAC) 是一個Objective-C語言內實現響應式編程的框架。框架

  RAC提供了大量的可以完成發送 value's stream 的API。異步

  RAC經過使用信號量(RACSignal)來完成獲取當前值和將來值的功能,而不像傳統的程序開發同樣須要聲明大量的變量。編程語言

 

Josh Abernathy這樣解釋它:ide

  • 程序接收輸入產生輸出。輸出就是對輸入作了一些事的結果。輸入,轉換,輸出,完成。
  • 輸入是應用動做的所有來源。點擊、鍵盤事件、定時器事件、GPS時間、網絡請求響應都算是輸入。這些事件被傳遞到應用中,應用將他們以某種方式混合,產生告終果:就是輸出。
  • 輸出一般會改變應用的UI。開關狀態變化、列表有了新的元素都是UI變化。也有可能讓磁盤上某個文件產生變化,或者產生一個API請求,這都是應用的輸出。
  • 但不像傳統的輸入輸出設計,應用的輸入輸出能夠產生不少次。應用打開後,不僅是一個簡單的 輸入→工做→輸出 就構成了一個生命週期。應用常常有大量的輸入並基於這些輸入產生輸出。

基本的使用方法

  例如,咱們目前想要實現一個NSString對象能夠一直綁定到最新的時間,即便字符串發生了變化,也不該該是再去使用時間去從新賦值了。函數式編程

  聽起來特別像Objective-C語言內的KVO特性,可是這並非具備壓倒性優點的那個方法:函數

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

  那麼咱們如今使用RAC框架來實現這個功能動畫

***.h
//用來標識時間變量
@property (nonatomic ,strong) NSDate *time;
//用來標識文字顯示區域
@property (nonatomic ,strong) IBOutlet UILabel *label;

  

***.m
    //申請註冊一個每一個1秒將會在主線程執行一次的信號量
    RACSignal *repeatSignal = [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] repeat];
    //爲信號量添加執行代碼端
    [repeatSignal subscribeNext: ^(NSDate* time){
        self.time = time;
    }];
    
    
    //申請註冊一個時間屬性的信號量
    RACSignal *timeSignal = [self rac_valuesForKeyPath:@"time" observer:self];
    //爲信號量添加執行代碼端
    [timeSignal subscribeNext:^(NSDate* time) {
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"HH:mm:ss"];
        
        self.label.text = [formatter stringFromDate:time];
        
        RELEASESAFELY(formatter);
    }];

  

  一樣和Objective-C語言內的KVO特性不一樣的是,RACSignal信號量能夠進行過濾設置。

  以上邊的例子的話,咱們加一個功能。

  •獲取偶數秒的時間

  那麼信號量部分的代碼能夠寫爲

    //申請註冊一個時間屬性的信號量
    RACSignal *timeSignal = [self rac_valuesForKeyPath:@"time" observer:self];
    //爲信號量添加過濾block
    [[timeSignal filter:^BOOL(NSDate* time) {
        
        //獲取描述的時間
        NSDateComponents *com = [[NSCalendar currentCalendar] components:NSCalendarUnitSecond fromDate:time];

        return com.second % 2 == 0;
        
    }] subscribeNext:^(NSDate* time) {
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"HH:mm:ss"];
        
        self.label.text = [formatter stringFromDate:time];
        
        RELEASESAFELY(formatter);
    }];

  

  信號量還能夠用來導出對應的狀態。與Objective-C語言KVO特性不一樣的是,RAC可以爲新的值設置其餘的屬性。

那麼咱們仍是舉個功能例子

  •在註冊用戶時,當用戶密碼與確認密碼相同時,在Label中顯示"1",不相同時,顯示"0";

  •如圖所示

  

  傳統方式代碼

- (BOOL)isValid {
    return 
            [self.password.text length] > 0 &&
            [self.confirm.text length] > 0 &&
            [self.password.text isEqual:self.confirm.text];
}

#pragma mark - UITextFieldDelegate
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{

    self.label.text = @(self.isValid).description;
    return YES;
}

  咱們發現邏輯被放在了不少方法裏,零碎地擺放在view controller裏,經過處處散佈到delegate裏的self.label.text = @(self.isValid).description;

方法在頁面的生命週期中被調用。

  那麼RAC實現方式的代碼

    RACSignal *passworkSignal = self.password.rac_textSignal;
    RACSignal *confirmSignal = self.confirm.rac_textSignal;

    RACSignal *combineSignal = [RACSignal combineLatest:@[passworkSignal,confirmSignal] reduce:^(NSString *password, NSString *confirm){
        ;
        return @([password isEqualToString:confirm]).description;
    }];
    
    RAC(self,label.text) = combineSignal;

  全部對於的輸入都整合在了一塊兒。每次不論哪一個輸入框被修改了,用戶的輸入都會被reduce成一個字符串的值,而後就能夠自動來控制註冊按鈕的可用狀態了。

  RAC除了可以完成KVO的功能以外,一樣能夠完成按鈕等用戶響應的交互功能

  •完成一個點擊按鈕彈出Alert的功能

  •如圖所示

  

  傳統方式實現的代碼

- (void)viewDidLoad {
    [super viewDidLoad];
    //添加觸發事件
    [self.btn addTarget:self action:@selector(didClick) forControlEvents:UIControlEventTouchUpInside];
}
//點擊按鈕觸發的回調方法
- (void)didClick
{
    //建立彈出窗口
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"藍鷗" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"肯定", nil];
    [alertView show];
    
    RELEASESAFELY(alertView);
}

  RAC方式實現的代碼以下

    //添加觸發信號量
    self.btn.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        
        //建立彈出窗口
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"藍鷗" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"肯定", nil];
        [alertView show];
        
        RELEASESAFELY(alertView);
        
        return [RACSignal empty];
    }];

  

  經過以上的代碼,RACSignal信號量具備以下功能

  • 異步控制或事件驅動的數據源:Cocoa編程中大多數時候會關注用戶事件或應用狀態改變產生的響應。
  • 鏈式以來操做:網絡請求是最多見的依賴性樣例,前一個對server的請求完成後,下一個請求才能構建。
  • 並行獨立動做:獨立的數據集要並行處理,隨後還要把他們合併成一個最終結果。這在Cocoa中很常見,特別是涉及到同步動做時。

  RACSignal會觸發它們的subscriber三種不一樣類型的事件:

  • 下一個事件從stream中提供一個新值。不像Cocoa集合,它是徹底可用的,甚至一個signal能夠包含 nil
  • 錯誤事件會在一個signal結束以前被標示出來這裏有一個錯誤。這種事件可能包含一個 NSError 對象來標示什麼發生了錯誤。錯誤必須被特殊處理——錯誤不會被包含在stream的值裏面。
  • 完成事件標示signal成功結束,不會再有新的值會被加入到stream當中。完成事件也必須被單獨控制——它不會出如今stream的值裏面。

  一個RACSignal信號量的生命週期由不少下一個(next)事件和一個錯誤(error)完成(completed)事件組成(後二者不一樣時出現)。

總結對比

   RAC 與 KVO

  Key-Value Observing是Cocoa全部魔法的核心,它被普遍應用在ReactiveCocoa對於屬性變化的影響動做中。然而KVO用起來即不簡單也不開心:它的API有不少過分設計的參數,以及缺少方便的block方式調用。

  RAC 與 Bindings

  Bindings也是黑魔法。

  雖然對OS X控制的要點就是Bindings,可是它的意義在近年來愈來愈沒那麼重要了,由於焦點已經移動到了iOS和UIKit這些Bindings不支持的東西身上。Bindings替代了大量的模版膠水代碼,容許在Interface Builder中完成編碼,但嚴格上說仍是比較有侷限性的,而且_沒法_debug。RAC提供了一種簡潔易懂、擴展性強的以代碼爲基礎的API來運行在iOS上,目標就是取代全部在OS X能用Bindings實現的神奇功能。

 

  Objective-C在C的核心上吸取了Smalltalk的思想創建而成,但哲學理念上已經超越了它本來來源的血統。

  @protocol 是對C++多重繼承的拒絕,順應抽象數據的類型範式是對Java Interface的吸取。Objective-C 2.0引入了@property / @synthesize則靈感來自C#的 get; set; 方法對getter和setter的速記(就語法上來講,這也是NeXTSETP強硬路線堅持者常常辯論的一點)。Block給這門語言帶來了函數式編程的好處,可使用Grand Central Dispatch——來自Fortran / C / C++ standard OpenMP思想而成的基於隊列的併發API。下標和對象字面量都是像Ruby、Javascript這樣的腳本語言的標準特性,現在也由一個Clang插件被帶入了Objective-C的世界裏。

  ReactiveCocoa則給Objective-C帶來了函數響應式編程的健康藥劑。它自己也是受C#的Rx library、Clojure和Elm的影響發展而成。

好的點子會傳染。ReactiveCocoa就是一種警示,提醒人們好的點子也能夠從看似不太可能的地方傳播過來,這樣的新鮮思想對解決相似的問題也會有徹底不一樣的方法呢。

   

  下一節,咱們來一塊兒看一下如何使用RAC來完成異步的功能.

相關文章
相關標籤/搜索