代码之家  ›  专栏  ›  技术社区  ›  Ian Dundas

ReactiveCocoa间隔计时器如何产生这种效果?

  •  1
  • Ian Dundas  · 技术社区  · 10 年前

    我正在学习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] 只调用一次,因此它不会像预期的那样每次都神奇地调用该方法。

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

    1 回复  |  直到 10 年前
        1
  •  2
  •   Ash Furrow    10 年前

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

    如果您要将日志代码稍微增加到以下内容:

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

    然后你会看到 value 对象每次涉及区块时 NSDate 实例正在生成并作为值发送。 NS日期 毕竟,对象是不可变的。

    好了,那么你的问题是:这是怎么发生的?好吧,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,以便使用新的 NS日期 实例。

    所以最后一块拼图是使用 startWith: . interval:scheduler: 设置一个信号,在 interval 秒。在此之前,信号没有发送任何信息,文本字段的值将为 nil 。所以我们使用 开始方式: 以便启动信号并使其立即发送值。