Car*_*son 14 cocoa-touch objective-c reactive-programming reactive-cocoa racsignal
我正在使用ReactiveCocoa更新UILabel一个UIProgressView倒计时:
NSInteger percentRemaining = ...;
self.progressView.progress = percentRemaining / 100.0;
__block NSInteger count = [self.count];
[[[RACSignal interval:0.05 onScheduler:[RACScheduler mainThreadScheduler]]
take: percentRemaining]
subscribeNext:^(id x) {
count++;
self.countLabel.text = [NSString stringWithFormat:@"%d", count];
self.progressView.progress = self.progressView.progress - 0.01;
} completed:^{
// Move along...
}];
Run Code Online (Sandbox Code Playgroud)
这种方法运行得很好但是,我对count变量或者读取值都不是特别满意,self.progressView.progress以便减少它.
我觉得我应该能够直接使用RAC宏来吐出信号并绑定属性.就像是:
RACSignal *baseSignal = [[RACSignal interval:0.05 onScheduler:[RACScheduler mainThreadScheduler]]
take: percentRemaining]
RAC(self, countLabel.text) = [baseSignal
map: ...
...
RAC(self, progressView.progress) = [baseSignal
map: ...
...
Run Code Online (Sandbox Code Playgroud)
这...揭示了我被困的地方.我不能完全理解如何编写RACSignal这样的东西,以至于我不需要依赖状态变量.
另外,我不知道在// Move along...流完成时注入我需要的副作用的位置/方式.
一旦你想到正确的方法,我确信两者都很简单但是,任何帮助都会非常感激.
Jus*_*ers 38
如有疑问,请查看 RACSignal + Operations.h 和 RACStream.h,因为必须有一个运营商来完成您的工作.在这种情况下,基本的缺失部分是 -scanWithStart:reduce : .
首先,让我们来看看baseSignal.逻辑将基本保持不变,除了我们应该
为它发布连接:
RACMulticastConnection *timer = [[[RACSignal
interval:0.05 onScheduler:[RACScheduler mainThreadScheduler]]
take:percentRemaining]
publish];
Run Code Online (Sandbox Code Playgroud)
这样我们就可以在所有相关信号之间共享一个定时器.虽然baseSignal你提供的也可以工作,但它会为每个用户重新创建一个计时器(包括相关信号),这可能会导致他们的射击变化很小.
现在,我们可以使用-scanWithStart:reduce:递增countLabel
和递减progressView.此运算符采用先前的结果和当前值,并允许我们根据需要转换或组合它们.
但在我们的例子中,我们只想忽略当前值(NSDate发送者+interval:),因此我们可以操纵前一个值:
RAC(self.countLabel, text) = [[[timer.signal
scanWithStart:@0 reduce:^(NSNumber *previous, id _) {
return @(previous.unsignedIntegerValue + 1);
}]
startWith:@0]
map:^(NSNumber *count) {
return count.stringValue;
}];
RAC(self.progressView, progress) = [[[timer.signal
scanWithStart:@(percentRemaining) reduce:^(NSNumber *previous, id _) {
return @(previous.unsignedIntegerValue - 1);
}]
startWith:@(percentRemaining)]
map:^(NSNumber *percent) {
return @(percent.unsignedIntegerValue / 100.0);
}];
Run Code Online (Sandbox Code Playgroud)
在-startWith:上述运营商看起来是多余的,但是这是必要的,以确保text与progress被之前设置timer.signal已派出什么.
然后,我们将使用正常订阅完成.完全有可能这些副作用也可以变成信号,但是如果没有看到代码就很难知道:
[timer.signal subscribeCompleted:^{
// Move along...
}];
Run Code Online (Sandbox Code Playgroud)
最后,因为我们使用了RACMulticastConnection上面的内容,实际上什么都不会发生.必须手动启动连接:
[timer connect];
Run Code Online (Sandbox Code Playgroud)
这将连接所有上述订阅,并启动计时器,因此值开始流向属性.
现在,这显然是比命令式等价物更多的代码,所以人们可能会问为什么它值得.有几个好处:
基本上,这是命令式与函数式编程的典型示例.
尽管命令式代码的起点不那么复杂,但它的复杂性 却呈指数级增长.功能代码(尤其是功能性反应代码)可能起初更复杂,但随后其复杂性呈线性增长- 随着应用程序的增长,管理起来要容易得多.
| 归档时间: |
|
| 查看次数: |
3385 次 |
| 最近记录: |