我正在学习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]
只被调用一次,因此每次都不会像预期的那样神奇地调用该方法。
我看不到手动递增日期的代码。那怎么发生呢?
答案 0 :(得分:2)
好问题。让我们拆开你的代码吧。
如果您要将日志记录代码略微增加到以下内容:
NSLog (@"Value(%p): %@", value, value);
然后你会看到value
对象的指针。每次涉及块时,都会生成一个新的NSDate
实例并将其作为值发送。毕竟,NSDate
个对象是不可变的。
好的,等等你的问题:这是怎么回事?好吧,ReactiveCocoa的好处在于它的开源,所以我们不必猜测它是怎么回事。设置重复的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:
来启动信号并让它立即发送一个值。