ReactiveCocoa 談談concat,reactivecocoaconcat

來源:互聯網
上載者:User

ReactiveCocoa 談談concat,reactivecocoaconcat

今天的一個商務程序,商務程序大概就是這樣的

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 [[[selfignoreValues]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

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.