今天的一個業務流程,業務流程大概就是這樣的
1.從CoreData中獲取之前的數據
2.更新界面
3.從網絡獲取數據
4.判斷獲取結果
5.處理錯誤判斷
6.更新界面
7.判斷結果numberOfNews字段
8.現實numberOfNews信息
這種順序行的處理,正正是ReactiveCocoa的擅長解決的問題,那么問題來了,怎么才能通過Signal,將if else 轉換數據,要知道,很多地方都在block里面
這就需要用到flattenMap 和 then 這兩個東西
來看看React的玩法
1 //1.從CoreData中獲取數據 2 RACSignal *local = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { 3 //1.1 獲取完成后通知下一步 4 [subscriber sendNext:nil]; 5 [subscriber sendCompleted]; 6 return nil; 7 }]; 8 9 //2.轉換數據,這個過程沒有理由在mainThread中進行的10 RACSignal *viewModel = [[local subscribeOn:[RACScheduler scheduler]] map:^id(id value) {11 //1.2 將CoreDataModel轉換成視圖模型12 return nil;13 }];14 15 //3.顯示到界面中16 [viewModel subscribeNext:^(id x) {17 18 19 }];20 21 //4.創建一個網絡請求22 RACSignal *request = [viewModel then:^RACSignal *{23 return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {24 25 NSURLsessionTask *task = nil;//這里新建一個網絡請求26 27 return [RACDisposable disposableWithBlock:^{28 if (task.state != NSURLSessionTaskStateCompleted) {29 [task cancel];30 }31 }];32 33 }];34 35 }];36 37 38 39 //5.避免重復請求,使用MutileConnection轉換Signal40 RACMulticastConnection *requestMutilConnection = [request multicast:[RACReplaySubject subject]];41 [requestMutilConnection connect];42 43 //6.處理服務器結果44 RACSignal *response = [request flattenMap:^RACStream *(id value) {45 //比如response中包含一個state 的枚舉字段,判斷這貨是返回是否有效請求46 47 // return [RACSignal return:value];48 return [RACSignal error:value];49 }];50 51 //7.更新界面52 [response subscribeNext:^(id x) {53 //再次更新界面54 }];55 56 //8.處理錯誤57 [response subscribeError:^(NSError *error) {58 //處理錯誤59 }];
當然,為了簡化,里面留了個坑,并且省略許多邏輯代碼
回到正題,concat 是 RACSignal 的一個實例方法
在源碼實現如下
- (RACSignal *)concat:(RACSignal *)signal { return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) { RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init]; RACDisposable *sourceDisposable = [self subscribeNext:^(id x) { [subscriber sendNext:x]; } error:^(NSError *error) { [subscriber sendError:error]; } completed:^{ RACDisposable *concattedDisposable = [signal subscribe:subscriber]; serialDisposable.disposable = concattedDisposable; }]; serialDisposable.disposable = sourceDisposable; return serialDisposable; }] setNameWithFormat:@"[%@] -concat: %@", self.name, signal];}
上面的代碼
1.創建一個新的信號
2.在原來的信號中訂閱subscribeNext 并在completed block中將新建的Signal的subscriber傳入到我們concat的信號
這里非常容易理解為什么可以在上一個信號完成時接著調用下一個信號,原因就在 signal subscribe:subscriber這里啊
但是事情并非這么簡單
再看看如果使用concat 時會怎么樣
一個非常簡單粗暴的代碼段
1 RACSignal *fristSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { 2 3 NSLog(@"oneSignal createSignal"); 4 [subscriber sendNext:@""]; 5 [subscriber sendCompleted]; 6 7 return [RACDisposable disposableWithBlock:^{ 8 NSLog(@"oneSignal dispose"); 9 }];10 }];11 12 RACMulticastConnection *connection = [fristSignal multicast:[RACReplaySubject subject]];13 14 [connection connect];15 16 [connection.signal subscribeNext:^(id x) {17 NSLog(@"2");18 }];19 20 21 RACSignal *afterConcat = [connection.signal concat:[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {22 [subscriber sendNext:@""];23 return nil;24 }]];25 26 [afterConcat subscribeNext:^(id x) {27 NSLog(@"afterConcat subscribeNext");28 }];
輸出結果
2015-10-15 23:00:26.998 conatAndThen[3814:2388477] oneSignal createSignal2015-10-15 23:00:26.999 conatAndThen[3814:2388477] oneSignal dispose2015-10-15 23:00:27.001 conatAndThen[3814:2388477] 22015-10-15 23:00:27.001 conatAndThen[3814:2388477] afterConcat subscribeNext2015-10-15 23:00:27.002 conatAndThen[3814:2388477] afterConcat subscribeNext
afterConcat 的 subscribNext被調用了兩次!!!
在來看看then
1 RACSignal *fristSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { 2 3 NSLog(@"oneSignal createSignal"); 4 [subscriber sendNext:@""]; 5 [subscriber sendCompleted]; 6 7 return [RACDisposable disposableWithBlock:^{ 8 NSLog(@"oneSignal dispose"); 9 }];10 }];11 12 RACMulticastConnection *connection = [fristSignal multicast:[RACReplaySubject subject]];13 14 [connection connect];15 16 [connection.signal subscribeNext:^(id x) {17 NSLog(@"2");18 }];19 20 21 RACSignal *then = [connection.signal then:^RACSignal *{22 23 return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {24 [subscriber sendNext:@""];25 return nil;26 }];27 28 }];29 30 [then subscribeNext:^(id x) {31 NSLog(@"then subscribNext");32 }];
輸出結果
2015-10-15 23:02:40.746 conatAndThen[3848:2419019] oneSignal createSignal2015-10-15 23:02:40.747 conatAndThen[3848:2419019] oneSignal dispose2015-10-15 23:02:40.748 conatAndThen[3848:2419019] 22015-10-15 23:02:40.750 conatAndThen[3848:2419019] then subscribNext
這才是我們想要的結果
then 實際上是對 concat 的包裝
我們看看源碼是怎么避免重復執行的
- (RACSignal *)then:(RACSignal * (^)(void))block { NSCParameterAssert(block != nil); return [[[self ignoreValues] concat:[RACSignal defer:block]] setNameWithFormat:@"[%@] -then:", self.name];}
關鍵就在ignoreValues 方法中
- (RACSignal *)ignoreValues { return [[self filter:^(id _) { return NO; }] setNameWithFormat:@"[%@] -ignoreValues", self.name];}
為了證明我的猜想,在demo中concat前filter一次
RACSignal *afterConcat = [[connection.signal filter:^BOOL(id value) { return NO; }] concat:[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@""]; return nil; }]];
結果如下
2015-10-15 23:09:51.013 conatAndThen[3967:2511660] oneSignal createSignal2015-10-15 23:09:51.013 conatAndThen[3967:2511660] oneSignal dispose2015-10-15 23:09:51.015 conatAndThen[3967:2511660] 22015-10-15 23:09:51.016 conatAndThen[3967:2511660] afterConcat subscribeNext
更深入的問題來了,為什么filter一次就可以避免重復發送
從源碼拷貝出來整理分析
RACSignal *after = [RACSignal createSignal:^(id<RACSubscriber> subscriber) { RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init]; RACDisposable *sourceDisposable = [connection.signal subscribeNext:^(id x) { [subscriber sendNext:x]; } error:^(NSError *error) { [subscriber sendError:error]; } completed:^{
RACDisposable *concattedDisposable = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@""];//試試把這個注釋, after subscribeNext 只會執行一次 return nil; }] subscribe:subscriber]; serialDisposable.disposable = concattedDisposable; }]; serialDisposable.disposable = sourceDisposable; return serialDisposable; }]; [after subscribeNext:^(id x) { NSLog(@"afterConcat subscribeNext"); }];
真相已經出現了
在completed block 中 創建的signal(SA),其subsciber (A)已經變成了外層的Signal 的 subsciber,而 connection.signal 中 的subscribeNext 已經對(A)sendNext 一次,而我們需要concat 的signal 需要通知訂閱這 在SA又sendNext一次, 所以 then 的出現就是避免 [subscriber sendNext:x]對外部執行流程的影響
參考文獻
https://github.com/ReactiveCocoa/ReactiveCocoa
http://tech.meituan.com/RACSignalSubscription.html
新聞熱點
疑難解答