【问题标题】:How does a ReactiveCocoa interval timer produce this effect?ReactiveCocoa 间隔计时器是如何产生这种效果的?
【发布时间】:2014-08-16 09:24:44
【问题描述】:

我正在学习 ReactiveCocoa,但被以下内容难住了:

如果我将此信号绑定到标签:

-(void)viewDidLoad{
    [super viewDidLoad];
    RAC(self.currentTimeLabel, text) = self.timeAgoSignal;
}

-(RACSignal *)timeAgoSignal{
    return [[[RACSignal interval:1 onScheduler:[RACScheduler scheduler]] startWith:[NSDate date]] map:^id (NSDate *value) {
        NSLog (@"Value: %@", value);
        return value.description;
    }];
}

首先,标签没有更新(我不关心这个,我可能可以找出原因)。我的主要问题是:块如何记录每个滴答的更新日期?

Value: 2014-08-16 09:15:40 +0000
Value: 2014-08-16 09:15:41 +0000
Value: 2014-08-16 09:15:42 +0000
Value: 2014-08-16 09:15:43 +0000

这对我来说太棒了。如果我有一个计时器输出 [NSDate 日期],它当然不会在每个刻度上改变:

-(instancetype)init{
    if (self = [super init]) {
        _date = [NSDate date];

        [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector (output) userInfo:nil repeats:YES];
    }
    return self;
}
-(void)output{
    NSLog (@"Date: %@", self.date);
}

输出:

Date: 2014-08-16 09:19:36 +0000
Date: 2014-08-16 09:19:36 +0000
Date: 2014-08-16 09:19:36 +0000
Date: 2014-08-16 09:19:36 +0000

这是预期的行为。

如果startWith:[NSDate date] 位于一个块中,并且在每次滴答时都被重新调用,那么我可以理解。但事实并非如此:

例如,如果我改用 startWith:[self date],我可以看到 [self date] 只被调用一次,所以它不会像预期的那样每次都神奇地调用该方法。

我看不到任何代码手动增加日期。那么这是怎么发生的呢?

【问题讨论】:

  • 我实际上修复了“不更新标签”位:我需要将deliverOn:[RACScheduler mainThreadScheduler] 附加到信号中,以便 UI 真正重绘。更多详情at the bottom of this post

标签: reactive-cocoa


【解决方案1】:

好问题。让我们拆开你的代码。

如果您将日志记录代码稍微增加如下:

NSLog (@"Value(%p): %@", value, value);

然后你会看到value 对象的指针。每次涉及块时,都会生成一个新的NSDate 实例并将其作为值发送。毕竟,NSDate 对象是不可变的。

好的,继续你的问题:这是怎么回事?好吧,ReactiveCocoa 的好处是它的开源,所以我们不必猜测发生了什么。设置repetition的code其实是使用了你传入的调度器,我们来看看that code

uint64_t intervalInNanoSecs = (uint64_t)(interval * NSEC_PER_SEC);
uint64_t leewayInNanoSecs = (uint64_t)(leeway * NSEC_PER_SEC);

dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue);
dispatch_source_set_timer(timer, [self.class wallTimeWithDate:date], intervalInNanoSecs, leewayInNanoSecs);
dispatch_source_set_event_handler(timer, block);
dispatch_resume(timer);

return [RACDisposable disposableWithBlock:^{
    dispatch_source_cancel(timer);
    dispatch_release(timer);
}];

所以调度程序只是使用 GCD 来设置循环块调用,每次都使用新的 NSDate 实例。

所以最后一个难题是startWith: 的使用。 interval:scheduler: 设置一个信号,在 interval 秒后触发它的第一个事件。在那之前,信号还没有发送任何东西,您的文本字段的值将是nil。所以我们使用startWith: 来启动信号并让它立即发送一个值。

【讨论】:

  • 啊哈!因为我很困惑,所以我没有正确地提出我的问题,但你回答的最后一部分让它点击了。我错误地假设 [NSDate date] 对象以某种方式被保留并递增(并且,正如你所说,它们应该是不可变的),我找不到代码。我没有注意的是 NSDate* 传递给 map: 实际上是来自 interval:onScheduler: (尽管现在很明显:这一定是目的。我认为这只是一个计时器),并且startsWith: 参数只加载一个 initial 值。谢谢阿什。
猜你喜欢
  • 2013-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-13
  • 1970-01-01
  • 2021-11-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多