1. 程式人生 > >函式式響應式程式設計框架ReactiveCocoa總結

函式式響應式程式設計框架ReactiveCocoa總結

ReactiveCocoa與函式響應式程式設計
在Cocoa框架下的的函式響應式程式設計框架。Github mac客戶端產物,富含cocoa框架多種元件,提供基於時間變化的資料流的組合和變換。

指令式程式設計vs函式式響應式程式設計
指令式程式設計用語言提供的操作命令編寫操作序列給電腦執行。
函數語言程式設計:將電腦的運算視為函式的運算, 包含如下特性:
閉包和高階函式:函式可以作為物件,作為引數輸入和作為結果返回
惰性計算:表示式值不需要繫結的時候計算,在求值程式需要產生表示式的值時進行計算。
遞迴:通過遞迴,避免定義狀態變數。
不修改狀態:不改變系統的外部狀態,內部不定義狀態變數
沒有副作用:副作用指修改系統的狀態,影響其它模組,函數語言程式設計都是表示式語句,不包含任何賦值語句,變數值一 旦被指派,就永遠不會改變
和指令式程式設計相比,函數語言程式設計由表示式組成,內部不含狀態變化,內部不含有變數,通過遞迴解決問題。
函數語言程式設計好處:
1. 程式碼簡潔,開發快速
2. 接近自然語言,易於理解
3. 程式碼方便管理
4. 易於”併發程式設計”
5. 程式碼的熱升級
響應式程式設計:一種面向資料流和變化傳播的程式設計正規化,物件和物件之間的相互影響,一個物件變化關聯的物件會跟著變化。一個變化抽象為一種訊號,產生資料流變化。
1.對訊號產生的資料流值的操作
2.訊號產生的資料流的數量操作
3.訊號產生的資料流的緯度操作
4.訊號產生的資料流的合併處理

ReactiveCocoa使用

//target--action
    [[textField rac_textSignal] subscribeNext:^(UITextField *textField) {
        NSLog(@"%@",textField);
    }];
    [[textField rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(UITextField *field) {
        NSLog(@"%@",field.text);
    }];

    //notification
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"111" object:nil] subscribeNext:^(id x) { NSLog(@"%@",x); }]; [[NSNotificationCenter defaultCenter] postNotificationName:@"111" object:@"111"]; //kvo [RACObserve(self,title) subscribeNext:^(id x) { NSLog
(@"%@",x); }]; self.title = @"xxxx"; //delegate UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"gogogo" message:@"gogogo" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil]; [[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple *tuple) { NSLog(@"%@",tuple.first); NSLog(@"%@",tuple.second); NSLog(@"%@",tuple.third); }]; [alertView show]; //冷訊號 建立訊號 傳送訊號 訂閱訊號 RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"傳送值"]; return [RACScopedDisposable disposableWithBlock:^{ NSLog(@"被銷燬"); }]; }]; [signal subscribeNext:^(id x) { NSLog(@"接受到訊號的值"); }]; //熱訊號 建立 訂閱 傳送訊號 RACSubject *subject = [RACSubject subject]; [subject subscribeNext:^(id x) { NSLog(@"收到傳送的訊號"); }]; [subject subscribeNext:^(id x) { NSLog(@"收到傳送的訊號"); }]; [subject sendNext:@"11111"]; //元組 遍歷 效率太差 慎用 NSArray *array = @[@"123",@"1234",@"2356"]; [array.rac_sequence.signal subscribeNext:^(id x) { NSLog(@"%@",x); }]; //RACCOMMAND RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"hhahahahah"]; return [RACDisposable disposableWithBlock:^{ }]; }]; return signal; }]; [command execute:@"111"]; //Racmuticastconnection RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"hhahahahah"]; return [RACDisposable disposableWithBlock:^{ NSLog(@"-----"); }]; }]; RACMulticastConnection *connection = [signalA publish]; [connection.signal subscribeNext:^(id x) { NSLog(@"----------------x"); }]; [connection.signal subscribeNext:^(id x) { NSLog(@"-----------------x"); }]; [connection connect]; //常用的巨集定義 [[RACObserve(self, title) replayLazily] subscribeNext:^(id x) { NSLog(@"%@",x); }]; //繫結 RAC(self ,title) = [textField.rac_textSignal replayLazily]; [textField.rac_textSignal subscribeNext:^(id x) { NSLog(@"%@",x); }]; [[[textField rac_textSignal] bind:^RACStreamBindBlock{ return ^RACStream *(id value, BOOL *stop) { return [RACReturnSignal return:[NSString stringWithFormat:@"輸出:%@",value]]; }; }] subscribeNext:^(id x) { NSLog(@"%@",x); }]; //MAP [[textField.rac_textSignal map:^id(id value) { return [NSString stringWithFormat:@"11111%@",value]; }] subscribeNext:^(id x) { NSLog(@"%@",x); }]; /// faltenmap [[textField.rac_textSignal flattenMap:^RACStream *(id value) { return [RACReturnSignal return:value]; }] subscribeNext:^(id x) { NSLog(@"dsdddd%@",x); }]; //contact 先發送訊號a ,型號a傳送完成 才傳送 signal RACSignal *signacContact = [signalA concat: signal]; [signacContact subscribeNext:^(id x) { NSLog(@"%@",x); }]; //merge 多個訊號合併為一個訊號,任何一個訊號有新值都會呼叫 RACSignal *signacMerge = [signalA merge:signal]; [signacMerge subscribeNext:^(id x) { NSLog(@"%@",x); }]; //zipwith 合併兩個訊號,並把兩個訊號內容合併為元組 RACSignal *zipSignal = [signalA zipWith:signal]; [zipSignal subscribeNext:^(id x) { NSLog(@"%@",x); }]; //過濾 [textField.rac_textSignal filter:^BOOL(NSString *value) { return value.length > 3; }]; [textField.rac_textSignal ignore:@"3"]; [[textField.rac_textSignal distinctUntilChanged] subscribeNext:^(id x) { }]; [[textField.rac_textSignal skip:3] subscribeNext:^(id x) { }]; [[textField.rac_textSignal takeLast:3] subscribeNext:^(id x) { }]; [textField.rac_textSignal takeUntil:signal]; //秩序 [[textField.rac_textSignal doNext:^(id x) { }] doCompleted:^{ }]; //多執行緒 racSchedule [[RACScheduler scheduler] afterDelay:0 schedule:^(void) { }]; [[signal deliverOn:[RACScheduler scheduler]] subscribeNext:^(id x) { }]; [signal subscribeOn:[RACScheduler scheduler]]; //時間 timeout delay interval [textField.rac_textSignal timeout:0.01 onScheduler:[RACScheduler currentScheduler]]; [textField.rac_textSignal delay:1.0]; [textField.rac_textSignal subscribeError:^(NSError *error) { NSLog(@"error"); }]; [[RACSignal interval:1 onScheduler:[RACScheduler scheduler]] subscribeNext:^(id x) { NSLog(@"FDF"); }]; //RAC之重複 replay replaylazily RACSignal *signal_replay = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"1111"]; return nil; }] replayLazily]; [signal_replay subscribeNext:^(id x) { NSLog(@"11111"); }]; [signal_replay subscribeNext:^(id x) { NSLog(@"11111"); }]; //replaylast RACSubject *subjectaaa = [RACSubject subject]; RACSignal *signalaaaaa = [subjectaaa replayLast]; [subjectaaa sendNext:@"1111"]; [subjectaaa sendNext:@"33333"]; [signalaaaaa subscribeNext:^(id x) { NSLog(@"%@",x); }]; [subjectaaa sendNext:@"333"]; [signalaaaaa subscribeNext:^(id x) { NSLog(@"%@",x); }]; @weakify(self) [subjectaaa subscribeNext:^(id x) { @strongify(self); }];

踩坑總結

 //1.RACObserve 帶來的迴圈引用
    [subjectaaa subscribeNext:^(id x) {
        @strongify(self)
        RACObserve(self, title);
    }];
    //RACSubject 進行了 map、filter、merge、combineLatest、flattenMap轉換操作後,要傳送complete ,不然無法釋放
    [[subjectaaa map:^id(NSNumber *value) {
        return @([value integerValue] *3);
    }] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    [subjectaaa sendNext:@(123)];
    [subjectaaa sendCompleted];
    //signal造成的多次訂閱問題,使用reply,replayLazily讓subscriber裡面的程式碼只被執行一次

小結
RACCommand作用
重複執行一個過程
開關控制
防止重入
結果統一處理

屬性
executionSignals 所有的執行訊號,使用switchToLatest(獲取訊號中的訊號)降階
excuting 是否正在執行command
enabled 開關訊號
errors 所有的錯誤訊號,對錯誤進行統一處理
allowsConcurrentExecution 是否支援併發執行

Racchannel 本質是一個signal
雙向通話
處理回聲問題

RAC巨集 : 將訊號產生的值和rac括號內的值繫結
RAC(self.outputLabel, text) = self.inputTextField.rac_textSignal;
重複繫結丟擲異常
takeuntil 取消繫結

什麼場景使用rac
1.非同步場景 (處理成功 失敗 錯誤 變換和組合 串聯 錯誤傳遞和捕獲 執行緒處理)
2.響應事件處理(ui時間 delegate)
3.切面處理(方法呼叫監聽)

副作用消除
在doNext中顯式宣告
只有訂閱才產生副作用
保證訊號變換返回新的不改變舊的

frp思維
需求分析
輸入 輸出
資料流圖

小技巧
instruments RAC instruments資料夾 新增除錯規則到instruments (主要是通過對rac的理解來解決問題)
nsmutableArray 通過mutableArrayForKey來取得值 監聽add remove方法
錯誤處理:使用raccommand統一處理 catch 轉發至subject
RAC是函式響應式程式設計框架,不是專門為mvvm提供繫結的工具。
demo