我写了一个非常简单的ReactiveCocoa测试应用程序,试着在RAC中编写代码(而不是只是无休止地阅读它).这是在Github上,我想得到一些关于它的具体问题.我将继续链接到代码组件.
首先,简要说明应用程序:它是一个计时器驱动的迭代计数器,可以由用户暂停.(其目的是计算已经过了多少秒,忽略了用户暂停它的那些秒.)每秒一次,如果用户没有暂停递增行为,则计时器会递增变量.
我关注的三个课程是听取反馈意见:
MPSTicker(.m),执行"从初始化开始累积,除非暂停"并在信号上提供该结果.它有一个公共BOOL
属性来控制累积是否正在运行.
MPSViewModel(.m),它提供ViewModel包装MPSTicker
到视图控制器.它提供只读字符串,显示刻度数并显示操作的文本,如果采用,则"暂停"或"恢复"刻度.它还具有BOOL
暂停/取消暂停刻度的读写.
MPSViewController(.m),它MPSViewModel
通过将标签绑定到ViewModel的tick字符串来消耗字符串,将按钮的文本绑定到"tick action"字符串,并将按钮的按下映射到ViewModel的暂停属性.
我的问题:
我不喜欢启用/禁用其累积的BOOL
属性MPSTicker
,但我不知道怎么做更多Reactive-ly.(这也在ViewModel和ViewController的下游运行:如何通过所有这三个字符串运行一个字符串来控制自动收报机是否正在运行?)
该视图模型公开tickString
和tickStateString
非常传统的性质,但它消耗的这些马上的ViewController映射它们放回文本上的标签和按钮文本用RACObserve
.这感觉不对,但我不知道如何从ViewModel公开一个信号,ViewController很容易为这两个属性使用.
翻转ViewModel 时,ViewController会受到侮辱paused
BOOL
.我认为这是#1的另一个下游效应,"这不应该是一个BOOL
属性",但我不确定
(注:我觉得我退避三舍从信号对BOOL
的paused
上MPSTicker
,因为我不知道如何使用它在视图模型推导出两个字符串(一个用于当前的时间计数,和一个用于操作文本),也不怎么当用户按下"暂停"或"恢复"按钮时,推动UI驱动的值更改.这是我在问题1和3中的核心问题.)
一些截图可以帮助您想象这个华丽的设计:
滴答:
已暂停:
这是一篇非常棒的文章!
我不喜欢MPSTicker上的BOOL属性启用/禁用它的累积,但我不知道怎么做更多Reactive-ly.(这也在ViewModel和ViewController的下游运行:如何通过所有这三个字符串运行一个字符串来控制自动收报机是否正在运行?)
从广义上讲,使用属性没有任何错误或不反应.可以将KVO能力属性视为学术FRP意义上的行为:它们是在其生命周期的所有点都具有价值的信号.实际上,在Objective-C中,属性甚至可以比信号更好,因为它们保留了类型信息,否则我们会将其包装在一起RACSignal
.
因此,如果它是适合工作的正确工具,那么使用KVO-able属性没有任何问题.只是倾斜你的头,眯一点,它们看起来像信号.
某些东西应该是属性还是RACSignal
更多关于你想要捕获的语义.您是否需要财产的属性(ha!),或者您是否更关心价值随时间变化的一般概念?
在具体的情况下MPSTicker
,我认为过渡accumulateEnabled
真的是你关心的事情.
所以如果MPSTicker
有accumulationEnabledSignal
房产,我们会做以下事情:
_accumulateSignal = [[[[RACSignal combineLatest:@[ _tickSignal, self.accumulationEnabledSignal ]] filter:^(RACTuple *t) { NSNumber *enabled = t[1]; return enabled.boolValue; }] reduceEach:^(NSNumber *tick, NSNumber *enabled) { return tick; }] scanWithStart:@(0) reduce:^id(NSNumber *previous, id next) { // On each tick, we add one to the previous value of the accumulate signal. return @(previous.unsignedIntegerValue + 1); }];
我们将tick和enableness结合起来,因为它是两者的转换驱动我们的逻辑.
(FWIW,RACCommand
类似并使用启用的信号:https://github.com/ReactiveCocoa/ReactiveCocoa/blob/9503c6ef7f2f327f4db6440ddfbc4ee09b86857f/ReactiveCocoaFramework/ReactiveCocoa/RACCommand.h#L95.)
ViewModel将tickString和tickStateString公开为非常传统的属性,但使用它们的ViewController会立即将它们映射回带有RACObserve的标签和按钮文本上的文本.这感觉不对,但我不知道如何从ViewModel公开一个信号,ViewController很容易为这两个属性使用.
我可能在这里错过了你的观点,但我认为你所描述的很好.这可以追溯到关于属性和信号之间关系的上述观点.
使用RAC和MVVM,许多代码只是将数据线程化到应用程序的其他部分,并根据需要在其特定上下文中进行转换.这是关于通过应用程序的数据流.它很无聊 - 几乎是机械的 - 但这有点重要.我们越需要以一种特殊的方式重新发明或处理,就越好.
FWIW,我稍微改变了实现:
RAC(self, tickString) = [[[[_ticker accumulateSignal] deliverOn:[RACScheduler mainThreadScheduler]] // Start with 0. startWith:@(0)] map:^(NSNumber *tick) { // Unpack the value and format our string for the UI. NSUInteger count = tick.unsignedIntegerValue; return [NSString stringWithFormat:@"%i tick%@ since launch", count, (count != 1 ? @"s" : @"")]; }];
这样我们就可以更明确地定义tickString
一些转换的关系ticker
(我们可以避免做强/弱self
舞).
在ViewModel上翻转暂停的BOOL时,ViewController会遭受侮辱.我认为这是#1的另一个下游效应,"这不应该是BOOL属性",但我不确定
我可能只是因为疲倦而错过了它,但你在这里想到的是什么?