-
1.ReactiveCocoa常見操作方法介紹
-
1.1 ReactiveCocoa操作須知
所有的信號(RACSignal)都可以進行操作處理,因為所有操作方法都定義在RACStream.h中,而RACSignal繼承RACStream。
-
-
1.2. ReactiveCocoa操作思想
- 運用的是Hook(鉤子)思想,Hook是一種用于改變API(應用程序編程接口:方法)執行結果的技術. - Hook用處:截獲API調用的技術。 - Hook原理:在每次調用一個API返回結果之前,先執行你自己的方法,改變結果的輸出。 - RAC開發方式:RAC中核心開發方式,也是**綁定**,之前的開發方式是賦值,而用RAC開發,應該把重心放在綁定,也就是可以在創建一個對象的時候,就綁定好以后想要做的事情,而不是等賦值之后在去做事情。 - 列如:把數據展示到控件上,之前都是重寫控件的setModel方法,用RAC就可以在一開始創建控件的時候,就綁定好數據。
-
1.3 ReactiveCocoa核心方法bind
1.3.1.ReactiveCocoa操作的核心方法是bind(綁定),給RAC中的信號進行綁定,只要信號一發送數據,就能監聽到,從而把發送數據改成自己想要的數據。
1.3.2.在開發中很少使用bind方法,bind屬于RAC中的底層方法,RAC已經封裝了很多好用的其他方法,底層都是調用bind,用法比bind簡單.
-
1.3.3.bind方法簡單介紹和使用。
bind方法簡單介紹和使用假設想監聽文本框的內容,并且在每次輸出結果的時候,都在文本框的內容拼接一段文字“輸出:”
方式一:在返回結果后,拼接。
[_textField.rac_textSignal subscribeNext:^(id x) {NSLog(@"輸出:%@",x); }];
方式二:在返回結果前,拼接,使用RAC中bind方法做處理。
bind方法參數:需要傳入一個返回值是RACStreamBindBlock的block參數 RACStreamBindBlock是一個block的類型,返回值是信號,參數(value,stop),因此參數的block返回值也是一個block。 RACStreamBindBlock: 參數一(value):表示接收到信號的原始值,還沒做處理 參數二(*stop):用來控制綁定Block,如果*stop = yes,那么就會結束綁定。 返回值:信號,做好處理,在通過這個信號返回出去,一般使用RACReturnSignal,需要手動導入頭文件RACReturnSignal.h。
bind方法使用步驟:
- 1.傳入一個返回值RACStreamBindBlock的block。
- 2.描述一個RACStreamBindBlock類型的bindBlock作為block的返回值。
- 3.描述一個返回結果的信號,作為bindBlock的返回值。
注意:在bindBlock中做信號結果的處理。
底層實現:
- 1.源信號調用bind,會重新創建一個綁定信號。
- 2.當綁定信號被訂閱,就會調用綁定信號中的didSubscribe,生成一個bindingBlock。
- 3.當源信號有內容發出,就會把內容傳遞到bindingBlock處理,調用bindingBlock(value,stop)
- 4.調用bindingBlock(value,stop),會返回一個內容處理完成的信號(RACReturnSignal)。
- 5.訂閱RACReturnSignal,就會拿到綁定信號的訂閱者,把處理完成的信號內容發送出來。
注意:不同訂閱者,保存不同的nextBlock,看源碼的時候,一定要看清楚訂閱者是哪個。
這里需要手動導入#import <ReactiveCocoa/RACReturnSignal.h>
,才能使用RACReturnSignal
。[[_textField.rac_textSignal bind:^RACStreamBindBlock{ // 什么時候調用: // block作用:表示綁定了一個信號. return ^RACStream *(id value, BOOL *stop){ // 什么時候調用block:當信號有新的值發出,就會來到這個block。 // block作用:做返回值的處理 // 做好處理,通過信號返回出去. return [RACReturnSignal return:[NSString stringWithFormat:@"輸出:%@",value]]; }; }] subscribeNext:^(id x) { NSLog(@"%@",x); }];
-
1.4ReactiveCocoa操作方法之映射(flattenMap,Map)
flattenMap,Map用于把源信號內容映射成新的內容。
-
1.4.1. flattenMap 用于信號中的信號,把源信號的內容映射成一個新的信號,信號可以是任意類型
flattenMap使用步驟:
1.傳入一個block,block類型是返回值RACStream,參數value
2.參數value就是源信號的內容,拿到源信號的內容做處理
3.包裝成RACReturnSignal信號,返回出去。
flattenMap底層實現:
0.flattenMap內部調用bind方法實現的,flattenMap中block的返回值,會作為bind中bindBlock的返回值。
1.當訂閱綁定信號,就會生成bindBlock。
2.當源信號發送內容,就會調用bindBlock(value, *stop)
3.調用bindBlock,內部就會調用flattenMap的block,flattenMap的block作用:就是把處理好的數據包裝成信號。
4.返回的信號最終會作為bindBlock中的返回信號,當做bindBlock的返回信號。
5.訂閱bindBlock的返回信號,就會拿到綁定信號的訂閱者,把處理完成的信號內容發送出來。代碼:
//1.創建信號 RACSubject *subject =[RACSubject subject]; //2.綁定信號 [[subject flattenMap:^RACStream *(id value) { // block 只要原信號發送內容就會調用 // value 就是源信號發送的內容 NSLog(@"11---%@",[NSThread currentThread]); value = [NSString stringWithFormat:@"JK %@",value]; // 返回信號用來包裝成修改內容的值 return [RACReturnSignal return:value]; }] subscribeNext:^(id x) { //3.訂閱信號,flattenMap中返回的是什么信號,訂閱的就是什么信號 NSLog(@"%@",x); NSLog(@"22---%@",[NSThread currentThread]); }]; //4.源信號發送信號 [subject sendNext:@"123"];
-
-
1.4.2.map簡單使用
Map作用:把源信號的值映射成一個新的值map使用步驟:
- 1.傳入一個block,類型是返回對象,參數是value
- 2.value就是源信號的內容,直接拿到源信號的內容做處理
- 3.把處理好的內容,直接返回就好了,不用包裝成信號,返回的值,就是映射的值。
Map底層實現:
- 0.Map底層其實是調用flatternMap,Map中block中的返回的值會作為flatternMap中block中的值。
- 1.當訂閱綁定信號,就會生成bindBlock。
- 2.當源信號發送內容,就會調用bindBlock(value, *stop)
- 3.調用bindBlock,內部就會調用flattenMap的block
- 4.flattenMap的block內部會調用Map中的block,把Map中的block返回的內容包裝成返回的信號。
- 5.返回的信號最終會作為bindBlock中的返回信號,當做bindBlock的返回信號。
- 6.訂閱bindBlock的返回信號,就會拿到綁定信號的訂閱者,把處理完成的信號內容發送出來。
代碼
//1.創建信號 RACSubject *subject =[RACSubject subject]; //2.綁定信號 [[subject map:^id(id value) { // 返回的內容就是你要映射的值 value = [NSString stringWithFormat:@"JK:%@",value]; return value; }]subscribeNext:^(id x) { NSLog(@"映射之后的值= %@",x); }]; //4.源信號發送信號 [subject sendNext:@"123"];
-
1.4.3.FlatternMap和Map的區別
- 1.FlatternMap中的Block返回信號。
- 2.Map中的Block返回對象。
- 3.開發中,如果信號發出的值不是信號,映射一般使用Map
- 4.開發中,如果信號發出的值是信號,映射一般使用FlatternMap。
-
1.4.3.總結:signalOfsignals(信號中的信號)用FlatternMap**
// 創建信號中的信號 RACSubject *signalOfsignals = [RACSubject subject]; RACSubject *signal = [RACSubject subject]; // 創建訂閱者 [[signalOfsignals flattenMap:^RACStream *(id value) { // 當signalOfsignals的signals發出信號才會調用 return value; }] subscribeNext:^(id x) { // 只有signalOfsignals的signal發出信號才會調用,因為內部訂閱了bindBlock中返回的信號,也就是flattenMap返回的信號。 // 也就是flattenMap返回的信號發出內容,才會調用。 NSLog(@"%@aaa",x); }]; // 信號的信號發送信號 [signalOfsignals sendNext:signal]; // 信號發送內容 [signal sendNext:@1];
- 1.4.4.信號中的信號創建訂閱者的3種方式
// 1.創建信號中的信號
RACSubject *signalOfsignals = [RACSubject subject];
RACSubject *signal = [RACSubject subject];
//2.創建訂閱者的種方式
//第1種方式
[signalOfsignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 第2種方式
[signalOfsignals subscribeNext:^(RACSignal *x) {
[x subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}];
// 第3種方式
[[signalOfsignals flattenMap:^RACStream *(id value) {
// 當signalOfsignals的signals發出信號才會調用
return value;
}] subscribeNext:^(id x) {
// 只有signalOfsignals的signal發出信號才會調用,因為內部訂閱了bindBlock中返回的信號,也就是flattenMap返回的信號。
// 也就是flattenMap返回的信號發出內容,才會調用。
//5.打印信號發送的內容(可以進行修改)
NSLog(@"%@aaa",x);
}];
// 3.信號的信號發送信號
[signalOfsignals sendNext:signal];
// 4.信號發送內容
[signal sendNext:@1];
-
1.5 ReactiveCocoa操作方法之組合
-
1.5.1.concat:按一定順序拼接信號,當多個信號發出的時候,有順序的接收信號。
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@1]; [subscriber sendCompleted]; return nil; }]; RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@2]; return nil; }]; // 把signalA拼接到signalB后,signalA發送完成,signalB才會被激活。 RACSignal *concatSignal = [signalA concat:signalB]; // 以后只需要面對拼接信號開發。 // 訂閱拼接的信號,不需要單獨訂閱signalA,signalB // 內部會自動訂閱。 // 注意:第一個信號必須發送完成,第二個信號才會被激活 [concatSignal subscribeNext:^(id x) { NSLog(@"%@",x); }];
注意:第一個信號必須發送完成
[subscriber sendCompleted]
;,第二個信號才會被激活,按順序打印出來.(前后按順序實現,打印)concat底層實現:
- 1.當拼接信號被訂閱,就會調用拼接信號的didSubscribe - 2.didSubscribe中,會先訂閱第一個源信號(signalA) - 3.會執行第一個源信號(signalA)的didSubscribe - 4.第一個源信號(signalA)didSubscribe中發送值,就會調用第一個源信號(signalA)訂閱者的nextBlock,通過拼接信號的訂閱者把值發送出來. - 5.第一個源信號(signalA)didSubscribe中發送完成,就會調用第一個源信號(signalA)訂閱者的completedBlock,訂閱第二個源信號(signalB)這時候才激活(signalB)。 - 6.訂閱第二個源信號(signalB),執行第二個源信號(signalB)的didSubscribe - 7.第二個源信號(signalA)didSubscribe中發送值,就會通過拼接信號的訂閱者把值發送出來.
-
1.5.2.then:用于連接兩個信號,當第一個信號完成,才會連接then返回的信號
then:用于連接兩個信號,當第一個信號完成,才會連接then返回的信號
注意使用then,之前信號的值會被忽略掉.(忽略掉前面的,只打印后面的)
底層實現:
1、先過濾掉之前的信號發出的值。
2、使用concat連接then返回的信號。then代碼
[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {[subscriber sendNext:@1]; [subscriber sendCompleted]; return nil; }] then:^RACSignal *{ return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@2]; return nil; }]; }] subscribeNext:^(id x) { // 只能接收到第二個信號的值,也就是then返回信號的值 NSLog(@"%@",x); }];
-
1.5.3.merge:把多個信號合并為一個信號,任何一個信號有新值的時候就會調用
merge:把多個信號合并成一個信號(沒有順序)
//創建多個信號 RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@1]; return nil; }]; RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@2]; return nil; }]; // 合并信號,任何一個信號發送數據,都能監聽到. RACSignal *mergeSignal = [signalA merge:signalB]; [mergeSignal subscribeNext:^(id x) { NSLog(@"%@",x); }];
merge底層實現:
- 1.合并信號被訂閱的時候,就會遍歷所有信號,并且發出這些信號。
- 2.每發出一個信號,這個信號就會被訂閱
- 3.也就是合并信號一被訂閱,就會訂閱里面所有的信號。
- 4.只要有一個信號被發出就會被監聽。-
1.5.4.zipWith:把兩個信號壓縮成一個信號,只有當兩個信號同時發出信號內容時,并且把兩個信號的內容合并成一個元組,才會觸發壓縮流的next事件::----夫妻關系
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@1]; return nil; }]; RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@2]; return nil; }]; // 壓縮信號A,信號B RACSignal *zipSignal = [signalA zipWith:signalB]; [zipSignal subscribeNext:^(id x) { NSLog(@"%@",x); }];
-
底層實現:
- 1.定義壓縮信號,內部就會自動訂閱signalA,signalB
- 2.每當signalA或者signalB發出信號,就會判斷signalA,signalB有沒有發出個信號,有就會把最近發出的信號都包裝成元組發出。
-
1.5.5.combineLatest(重要)
combineLatest:將多個信號合并起來,并且拿到各個信號的最新的值,必須每個合并的signal至少都有過一次sendNext,才會觸發合并的信號RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@1]; return nil; }]; RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@2]; return nil; }]; // 把兩個信號組合成一個信號,跟zip一樣,沒什么區別 RACSignal *combineSignal = [signalA combineLatestWith:signalB]; [combineSignal subscribeNext:^(id x) { NSLog(@"%@",x); }];
combineLatest底層實現:
- 1.當組合信號被訂閱,內部會自動訂閱signalA,signalB,必須兩個信號都發出內容,才會被觸發。
-
2.并且把兩個信號組合成元組發出。
combineLatest
-
1.6 ReactiveCocoa操作方法之過濾
- 1.6.1.filter過濾信號,使用它可以獲取滿足條件的信號
filter過濾信號#pragma mark filter -(void)filter { // 過濾: // 每次信號發出,會先執行過濾條件判斷. [[_textField.rac_textSignal filter:^BOOL(NSString *value) { return value.length > 3; }] subscribeNext:^(id x) { NSLog(@"%@",x); }]; }
- 1.6.2.ignore:忽略完某些值的信號
ignore:忽略完某些值的信號// ignore: 忽略一些值 // ignoreValues: 忽略所有的值 //1.創建信號 RACSubject *subject = [RACSubject subject]; //2.忽略一些 RACSignal *ignoreSignal = [subject ignore:@12];//ignore相當于判斷一下 //3.訂閱信號 [ignoreSignal subscribeNext:^(id x) { //5.打印果略后的數據 NSLog(@"%@",x); }]; //4.發送數據 [subject sendNext:@"1"];
-
1.6.3.take:從開始一共取N次的信號
take:從開始一共取N次的信號// 1、創建信號 RACSubject *signal = [RACSubject subject]; // 2、處理信號,訂閱信號 [[signal take:2] subscribeNext:^(id x) { NSLog(@"%@",x); }]; // 3.發送信號 [signal sendNext:@1]; [signal sendNext:@2]; [signal sendNext:@3];
-
1.6.4.takeLast:取最后N次的信號,前提條件,訂閱者必須調用完成,因為只有完成,就知道總共有多少信號
takeLast:取最后N次的信號// 1、創建信號 RACSubject *signal = [RACSubject subject]; // 2、處理信號,訂閱信號 [[signal takeLast:2] subscribeNext:^(id x) { NSLog(@"%@",x); }]; // 3.發送信號 [signal sendNext:@1]; [signal sendNext:@2]; [signal sendNext:@3]; // 必須設置發送完成 [signal sendCompleted];
-
1.6.5.takeUntil:(RACSignal *):獲取信號直到執行完這個信號,只要傳入信號發送完成后或者發送任意數據,就不能再接受源信號的內容
-
監聽文本框的改變,知道當前對象被銷毀
[_textField.rac_textSignal takeUntil:self.rac_willDeallocSignal];
-
信號處理
takeUntil// takeUntil: 只要傳入信號發送完成后或者發送任意數據,就不能再接受源信號的內容 // 1、創建信號 RACSubject *signal = [RACSubject subject]; RACSubject *subject = [RACSubject subject]; // 2、處理信號,訂閱信號 [[signal takeUntil:subject] subscribeNext:^(id x) { NSLog(@"%@",x); }]; // 3.發送信號 [signal sendNext:@1]; [signal sendNext:@2]; // [subject sendCompleted]; [subject sendNext:@"hello"]; [signal sendNext:@3];
-
1.6.6.distinctUntilChanged:當上一次的值和當前的值有明顯的變化就會發出信號,否則會被忽略掉
// 過濾,當上一次和當前的值不一樣,就會發出內容。 // 在開發中,刷新UI經常使用,只有兩次數據不一樣才需要刷新 [[_textField.rac_textSignal distinctUntilChanged] subscribeNext:^(id x) { NSLog(@"%@",x); }];
distinctUntilChanged
// 創建信號
RACSubject *subject = [RACSubject subject];
// 訂閱信號
[[subject distinctUntilChanged] subscribeNext:^(id x) {NSLog(@"%@",x); }]; [subject sendNext:@"1"]; [subject sendNext:@"2"]; [subject sendNext:@"2"]; [subject sendNext:@"1"];
- 1.6.7.skip:(NSUInteger):跳過幾個信號,不接受
skip:(NSUInteger):跳過幾個信號,不接受
-
1.6.8.switchToLatest:用于signalOfSignals(信號的信號),有時候信號也會發出信號,會在signalOfSignals中,獲取signalOfSignals發送的最新信號。
RACSubject *signalOfSignals = [RACSubject subject]; RACSubject *signal = [RACSubject subject]; [signalOfSignals sendNext:signal]; [signal sendNext:@1]; // 獲取信號中信號最近發出信號,訂閱最近發出的信號。 // 注意switchToLatest:只能用于信號中的信號 [signalOfSignals.switchToLatest subscribeNext:^(id x) { NSLog(@"%@",x); }];
-
1.7 ReactiveCocoa操作方法之秩序
doNext: 執行Next之前,會先執行這個Block
-
doCompleted: 執行sendCompleted之前,會先執行這個Block
[[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@1]; [subscriber sendCompleted]; return nil; }] doNext:^(id x) { // 執行[subscriber sendNext:@1];之前會調用這個Block NSLog(@"doNext");; }] doCompleted:^{ // 執行[subscriber sendCompleted];之前會調用這個Block NSLog(@"doCompleted");; }] subscribeNext:^(id x) { NSLog(@"%@",x); }];
-
1.8 ReactiveCocoa操作方法之線程
deliverOn: 內容傳遞切換到制定線程中,副作用在原來線程中,把在創建信號時block中的代碼稱之為副作用。
subscribeOn: 內容傳遞和副作用都會切換到制定線程中。
-
1.9 ReactiveCocoa操作方法之時間
-
1.9.1.timeout:超時,可以讓一個信號在一定的時間后,自動報錯
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { return nil; }] timeout:1 onScheduler:[RACScheduler currentScheduler]]; [signal subscribeNext:^(id x) { NSLog(@"%@",x); } error:^(NSError *error) { // 1秒后會自動調用 NSLog(@"%@",error); }];
timeout-
1.9.2.interval 定時:每隔一段時間發出信號
[[RACSignal interval:1 onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(id x) { NSLog(@"%@",x); }];
-
-
1.9.3.delay 延遲發送next
delay 延遲發送next[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@1]; return nil; }] delay:3] subscribeNext:^(id x) { NSLog(@"%@",x); }];
-
1.10. ReactiveCocoa操作方法之重復
-
1.10.1.retry重試 :只要失敗,就會重新執行創建信號中的block,直到成功.
__block int i = 0; [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { if (i == 10) { [subscriber sendNext:@1]; }else{ NSLog(@"接收到錯誤"); [subscriber sendError:nil]; } i++; return nil; }] retry] subscribeNext:^(id x) { NSLog(@"%@",x); } error:^(NSError *error) { }];
-
- 1.10.2.replay重放:當一個信號被多次訂閱,反復播放內容
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
return nil;
}] replay];
[signal subscribeNext:^(id x) {
NSLog(@"第一個訂閱者%@",x);
}];
[signal subscribeNext:^(id x) {
NSLog(@"第二個訂閱者%@",x);
}];
-
1.10.3.throttle節流:當某個信號發送比較頻繁時,可以使用節流,在某一段時間不發送信號內容,過了一段時間獲取信號的最新內容發出
throttle節流RACSubject *signal = [RACSubject subject]; // 節流,在一定時間(1秒)內,不接收任何信號內容,過了這個時間(1秒)獲取最后發送的信號內容發出。 [[signal throttle:5] subscribeNext:^(id x) { NSLog(@"%@",x); }]; [signal sendNext:@"我是在5s之后打印的"];