RAC處理網絡請求和界面交互

前言

眾所周知RAC學習曲線陡峭,作為新手的我也踩了不少坑.學習Rx的過程首先要學會基本的操作,然后再組合操作完成復雜的需求,就像習武先學基本的招式,再利用基本招式產生組合拳.網上關于RAC 組合拳的優質文章并不多,在網上所搜到的項目大部分對Rx的運用也非常粗糙單調,這里我介紹此人的一系列文章.這篇博客的目的是給自己的學習之路做做筆記,記錄一下踩的坑和得到的收獲,希望也能幫到有需要的人.

需求

在開發中,很多時候我們需要在N個網絡請求完畢之后再進行UI界面的更新.假設現在我們有A請求和B請求,并且這兩個請求是并行請求,沒有順序執行關系,在A,B請求完成之后刷新界面.

假設現在開始兩個網絡請求:

[self.viewModel.commandA execute:nil];
[self.viewModel.commandB execute:nil];

然后需要在網絡請求命令執行的時候轉菊花或者進行下拉刷新的UI動作.

由于有多個網絡請求,所以可以將多個請求的執行信號合并成一個信號.

@weakify(self);
[[RACSignal
 merge:@[
          self.viewModel.commandA.executing,
          self.viewModel.commandB.executing
         ]]
 subscribeNext:^(NSNumber *loading) {
     @strongify(self);
     NSLog(@"%@",loading);
     if (loading.boolValue) {
         [self.tableView.header beginRefreshing];
     } else {
         [self.tableView.header endRefreshing];
         [self.tableView reloadData];
     }
 }];

得到的log值為:

0
0
1
1
0
0

但是這種做法有問題,我們先看看merge操作的時序圖:

可以看到,merge操作所產生的新信號能夠在被訂閱時得到原信號們所產生的輸出值.也就是說我們subscribeNext訂閱到的值是兩個executing信號實時的輸出的集合.但是當其中一個網絡操作較另一個操作耗時時,其中一個執行信號輸出0表示執行完畢,并對界面進行了刷新,然而另一個操作N秒之后才會執行完成.如log所示,當第一個請求完成輸出0時,第二個請求還在進行.
這樣的情況顯然不是我們想要的結果.
因此,merge方法并不適合該場景,應該換成combineLatest.


combineLatest能在每個被訂閱信號發生變化的時候,都會取得每個信號的最新值應用在函數上.在我們的例子中,每個executing信號發生變化,我們還要對其他信號的狀態進行觀察,才能判斷現在所處的是否是刷新狀態.
我們將方法替換掉:

@weakify(self);
[[RACSignal
 combineLatest:@[
                 self.viewModel.commandA.executing,
                 self.viewModel.commandB.executing
                 ]]
 subscribeNext:^(NSNumber *loading) {
     @strongify(self);
     NSLog(@"%@",loading);
     if (loading.boolValue) {
         [self.tableView.header beginRefreshing];
     } else {
         [self.tableView.header endRefreshing];
        [self.tableView reloadData];
     }
  }];

此時的代碼運行會出錯,因為訂閱得到的值loading實際上是一個RACTuple,里面包含了每個被訂閱信號上最新的值.
其實我們只關心當前是不是處于網絡請求狀態,也就是說,我們只關心兩種情況.
第一種情況,RACTuple中的值都為0,說明網絡請求還沒開始,或者網絡請求已經結束.
第二種情況,RACTuple中有一個值為1,說明正在網絡請求中.
我們拿到這個RACTuple,可以做mapreduce操作,但還有更高效的做法.
實際上,這個操作就是邏輯或操作,真值表為:

A B OR
1 1 1
0 1 1
1 0 1
0 0 0

RAC中提供了or操作的方法.

@weakify(self);
[[[RACSignal
 combineLatest:@[
                 self.viewModel.commandA.executing,
                 self.viewModel.commandB.executing
                 ]]
 or]
 subscribeNext:^(NSNumber *loading) {
     @strongify(self);
     NSLog(@"%@",loading);
     if (loading.boolValue) {
         [self.tableView.header beginRefreshing];
     } else {
         [self.tableView.header endRefreshing];
         [self.tableView reloadData];
     }
  }];

此時log如下:

0
1
1
1
0

和最開始的log相比,為什么少了個剛開始的0呢?仔細看上方combineLatest的圖,當兩個信號都發送值時才有第一個值合并,這點又和merge有區別.
我們似乎完成了我們的目的,但代碼還可以繼續優化.比如剛開始得到的0,這應該是信號初始發出的,對于我們來說根本沒有用,我們可以排除這個值的干擾.

@weakify(self);
    [[[[RACSignal
       combineLatest:@[
                       self.viewModel.commandA.executing,
                       self.viewModel.commandB.executing
                       ]]
     or]
     skipWhileBlock:^BOOL(NSNumber *loading) {
         return ![loading boolValue];
     }]
     subscribeNext:^(NSNumber *loading) {
         @strongify(self);
         NSLog(@"%@",loading);
         if (loading.boolValue) {
             [self.tableView.header beginRefreshing];
         } else {
             [self.tableView.header endRefreshing];
             [self.tableView reloadData];
         }
     }];

我們增加了skipWhileBlock方法.當得到第一個返回值為yes之前,后面的subscribeNext將不會訂閱到值.


在第一個值為1之前,所有的值都丟棄.得到log:

1
1
1
0

很好,我們又進了一步.接下來還有點問題,連續三次的1也可以進行過濾,因為正在進行刷新狀態,不用每次得到1又進行刷新.

@weakify(self);
    [[[[[RACSignal
       combineLatest:@[
                       self.viewModel.commandA.executing,
                       self.viewModel.commandB.executing
                       ]]
     or]
     skipWhileBlock:^BOOL(NSNumber *loading) {
         return ![loading boolValue];
     }]
     distinctUntilChanged]
     subscribeNext:^(NSNumber *loading) {
         @strongify(self);
         NSLog(@"%@",loading);
         if (loading.boolValue) {
             [self.tableView.header beginRefreshing];
         } else {
             [self.tableView.header endRefreshing];
             [self.tableView reloadData];
         }
     }];

distinctUntilChanged這個方法很好理解.

distinctUntilChanged

此時的log:

1
0

完美!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容