亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

ReactiveCocoa談談concat

2019-11-14 18:08:33
字體:
來源:轉載
供稿:網友

今天的一個業務流程,業務流程大概就是這樣的

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

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
www.亚洲一二| 成人97在线观看视频| 成人av色在线观看| 欧美日韩亚洲精品一区二区三区| 中文字幕日本精品| 亚洲欧美一区二区三区在线| 4444欧美成人kkkk| 久久久久久午夜| 69视频在线免费观看| 中文字幕亚洲一区二区三区五十路| 91国产精品电影| 国产精品xxxxx| 欧美性猛交xxxx乱大交3| 欧美天天综合色影久久精品| 久久99精品久久久久久琪琪| 欧美日韩亚洲一区二| 在线性视频日韩欧美| 社区色欧美激情 | 欧美极品少妇与黑人| 国产精品69久久久久| 亚洲精选在线观看| 亚州精品天堂中文字幕| 青青草原一区二区| 亚洲 日韩 国产第一| 亚洲理论在线a中文字幕| 久久国产精品久久久| 国产欧美一区二区三区在线| 九九热精品视频| 久久中文精品视频| 亚洲精品国产精品久久清纯直播| 久久视频在线免费观看| 中文字幕亚洲欧美日韩2019| 精品一区二区三区三区| 欧美激情视频在线观看| 欧美成人精品在线播放| 日韩精品中文字幕有码专区| 久久精品国产精品亚洲| 欧美亚洲免费电影| 国产91成人在在线播放| 国产精品免费小视频| yellow中文字幕久久| 久久久久久亚洲精品不卡| 日韩精品中文字幕在线观看| 91在线视频九色| 欧美黄网免费在线观看| 久久久久久高潮国产精品视| 久久精品国产亚洲7777| 亚洲另类欧美自拍| 日韩三级影视基地| 日韩欧美999| 亚洲欧美日韩国产中文专区| 狠狠色狠狠色综合日日小说| 欧美色视频日本高清在线观看| 成人精品久久一区二区三区| 国产欧美va欧美va香蕉在| 中文字幕一区二区三区电影| 97人人模人人爽人人喊中文字| 亚洲精品按摩视频| 精品女同一区二区三区在线播放| 亚洲色图欧美制服丝袜另类第一页| 九九热精品视频国产| 欧美激情精品久久久久久大尺度| 国产97在线|亚洲| 成人久久精品视频| 亚洲香蕉伊综合在人在线视看| 欧美极品少妇与黑人| 91国产精品视频在线| 亚洲国产小视频在线观看| 亚洲精品电影久久久| 一区二区三区无码高清视频| 97视频在线观看成人| 亚洲第五色综合网| 欧美一区二三区| 一个人看的www久久| 色偷偷88888欧美精品久久久| 亚洲日本成人网| 亚洲free性xxxx护士白浆| 欧美视频免费在线观看| 77777少妇光屁股久久一区| 亚洲精品久久在线| 国产激情综合五月久久| 性夜试看影院91社区| 精品日韩中文字幕| 久久琪琪电影院| 中文字幕精品一区二区精品| 国外成人在线播放| 国产精品久久久久久亚洲调教| 欧美亚洲国产日本| 中文字幕一区二区三区电影| 亚洲第一二三四五区| 成人激情免费在线| 亚洲第一偷拍网| 久久综合伊人77777蜜臀| 91精品免费视频| 自拍偷拍亚洲一区| 疯狂做受xxxx欧美肥白少妇| 久久这里只有精品视频首页| 日韩视频第一页| 欧美激情亚洲自拍| 国产一区深夜福利| 国产69精品久久久久9999| 国产精品视频最多的网站| 在线免费观看羞羞视频一区二区| 77777少妇光屁股久久一区| 色综合色综合网色综合| 亚洲欧洲自拍偷拍| 一道本无吗dⅴd在线播放一区| 精品动漫一区二区三区| 精品国产视频在线| 深夜福利91大全| 中文字幕在线观看亚洲| 亚洲精品aⅴ中文字幕乱码| 91精品国产高清久久久久久| 一区二区三区精品99久久| 91网站在线看| 国产精品久久久久一区二区| 97精品国产97久久久久久免费| 这里只有精品久久| 国内精品久久久久久久| 夜夜嗨av色综合久久久综合网| 国产一区二区三区视频免费| 日韩久久精品成人| 国产日本欧美一区| 欧美综合一区第一页| 欧美激情一二三| 欧美日韩视频免费播放| 91综合免费在线| 日韩成人av一区| 精品一区二区三区电影| 欧美国产视频日韩| 久久777国产线看观看精品| 国产精品xxxxx| 日韩精品在线看| 国产欧美最新羞羞视频在线观看| 亚洲最大av网站| 成人深夜直播免费观看| 国产亚洲综合久久| 国产精品久久久久久超碰| 精品一区电影国产| 日韩禁在线播放| 国产精品免费一区二区三区都可以| 国产精品偷伦免费视频观看的| 亚洲香蕉成视频在线观看| 欧美日韩在线免费| 国产精品扒开腿做爽爽爽的视频| 欧美成人免费全部| 国产成人涩涩涩视频在线观看| 国产色婷婷国产综合在线理论片a| 日韩视频中文字幕| 日韩av一区在线观看| 国产精品久久久久影院日本| 日韩av最新在线| 欧美国产高跟鞋裸体秀xxxhd| 91精品久久久久久久久久久久久久| 国产大片精品免费永久看nba| 欧美性色视频在线| 中文字幕免费国产精品| 国产不卡视频在线| 亚洲在线免费看| 久久精品国产亚洲7777| 国产精品久久久久免费a∨大胸| 国产精品极品尤物在线观看| 成人免费xxxxx在线观看| 国产97人人超碰caoprom|