2015-02-11 27 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

相关问题