2015-02-11 28 views
2

想象我有兩個信號:一個便宜和昂貴的:ReactiveCocoa - 訂閱第二信號,如果第一個沒有任何價值,完成

RACSignal *localSignal;  // Cheap signal. Sends object without network request 
          // if possible, otherwise completes immediately. 

RACSingal *networkSignal; // Expensive one. Always sends data, 
          // but requires expensive network operation. 

現在我想創建一個信號,它發送的值從第一個信號(如果有的話)或訂閱第二個信號並從該信號發送數據。

下面的解決方案几乎給我我想要的,但它總是訂閱第二個昂貴的信號,即使從第一個信號獲取值,並忽略第二個信號的值。

[[localDataSignal concat:networkDataSignal] take:1]; 

有沒有辦法有效解決問題?

+1

我覺得這不應該訂閱昂貴的信號,而且這是一個處置錯誤。可能想要就RAC的項目提交一個問題。 – 2015-02-11 15:53:53

+0

剛剛發現問題。看到我的答案吼叫。我很厚,因爲本地信號在它所訂閱的同一個線程中工作,取:1甚至沒有機會處理整個序列。 – Slabko 2015-02-12 12:22:03

回答

3

我只是發現什麼問題。本地信號用在創建它,並呼籲同一個線程:

RACSignal *localSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { 
    NSLog(@"Perfoming local operation"); 
    [subscriber sendNext:@"local value"]; 
    [subscriber sendCompleted]; 
    return nil; 
}]; 

當我看到這個問題(我不知道100%)採取:1甚至不有機會處置級聯網絡信號。但是,如果我將本地信號安排在與主線程調度程序不同的地方,那麼可以這樣:1按預期工作 - 它在獲得第一個值後打破鏈。

簡單地說這個片段的工作:

[[[localSignal deliverOn:[RACScheduler scheduler]] concat:networkSignal] take:1] 

它給了我正是我想要的:本地信號的值,如果有的話,或訂閱的網絡信號,並給了我它的價值。在本地信號發送值的情況下,網絡信號永遠不會被訂閱。

經驗法則是檢查信號工作的線程,然後取:1應按預期工作。

更新-deliveOnMainThread也給出-take:處置隊列的機會。

+1

啊我明白了。 RAC 2的一個設計缺陷是,在訂閱期間同步發送的事件總是會在任何鏈條可以通過處置被破壞之前發生。因此,在這種情況下,它本身不是線程,它是'localSignal'在訂閱時以同步的方式發送事件。您不需要'deliverOn:'不同的調度器,甚至可以在'mainThreadScheduler'上執行。任何'deliverOn:'產生一個增加異步的GCD跳躍。或者,'subscribeOn:'可以工作,或者甚至延遲:0。 – 2015-02-12 17:09:35

+0

是的,那是我的想法,當我嘗試。你也是對的,deliverOnMainThread也適用。謝謝你指出:1問題,所以我可以玩一點。 – Slabko 2015-02-12 18:28:01

+0

實際上,我可以用這種方法看到的一個問題是,即使在主線程中調度,信號也是異步執行的。如果我將它用於UI操作,例如用於帶圖像的UICollectionViewCell,理論上,當我將圖像信號應用於單元格時,集合視圖會獲取其單元格,然後信號將圖像應用於該單元格。如果它發生在不同的同步幀中,我會看到閃爍。用我特殊的例子,我沒有看到,顯然,因爲我返回單元格和信號在相同的同步幀中應用圖像。 – Slabko 2015-02-12 18:41:19

1

您可以使用:

- (RACSignal *)catchTo:(RACSignal *)signal; 

像這樣:

[[localSignal catchTo:networkSignal] subscribeNext:^(id x) { 
    // If localSignal sends error, networkSignal will be subscribed. 
}]; 
+1

感謝您的回答。問題是本地信號不發送任何錯誤。它是完成還是返回一個值然後完成。因此,我需要看看第一個信號是否完成,但如果我沒有它的值,那麼我想要訂閱第二個信號(昂貴的)。 – Slabko 2015-02-11 15:29:46

+0

聽起來我的本地信號應該發送一個錯誤,以防它不能發送下一個值,不是嗎? – 2015-02-11 20:52:21

1

這有點難看,但在網絡信號之前,連接一個有延遲的空信號。我還沒有嘗試過,但它應該引入允許take防止networkSignal被訂閱的異步,在localSignal發送值的情況下。此方法不會影響遞送localSignal的值。

[[RACSignal concat:@[ 
    localSignal, 
    [[RACSignal empty] deliverOn:RACScheduler.mainThreadScheduler], 
    networkSignal 
] take:1] 

替代deliverOn:,你可能會如果讓這個代碼更易於理解考慮delay:

+0

謝謝!我發現我錯了,即使我將信號傳遞給主調度器,它仍然同步工作。我只是模擬了一些例子:https://db.tt/th1oXRWO。此外,如果我將信號傳遞到不同的調度程序,足夠邏輯,它將在單元格返回後設置該值:https://db.tt/N89bOO1W。在屏幕截圖中,您可以看到一種綜合測試,但我也嘗試過使用真正的UICollectionView和實際單元格 - 在數據源返回單元格之前,圖像被同步設置。我真的應該說你感謝你指出了正確的方向。 – Slabko 2015-02-12 20:54:30

+0

剛試過 - 'delay:0'和向不同線程傳遞信號一樣。 – Slabko 2015-02-12 20:55:28

相關問題