RAC实战3---定时器

RAC中的定时器

这次我们写一个demo来讲解RAC中的定时器的使用。
demo功能:点击button发送验证码,并开始倒计时,倒计时结束之前不允许再次点击button发送验证码。


1555928164670.jpg

上面是一个button,下面是一个textview,为的是验证滚动textview的时候,是不是影响timer的计时。

@interface ViewController ()
@property(nonatomic,weak) IBOutlet UIButton * btn;
@property(nonatomic,assign) NSInteger time;
@property(nonatomic,retain) RACDisposable * dispoable;
@end

把创建的button链接到btn属性,另外添加要用到的另外2个属性。然后把下面的代码放到
viewDidLoad的最后面。

    @weakify(self)
    [[_btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        @strongify(self)
        
        x.enabled = false;
        self.time = 10;
        
        //这个就是RAC中的GCD
        self.dispoable = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
            _time --;
            NSString * title = _time > 0 ? [NSString stringWithFormat:@"请等待 %d 秒后重试",_time] : @"发送验证码";
            [self.btn setTitle:title forState:UIControlStateNormal | UIControlStateDisabled];
            self.btn.enabled = (_time==0)? YES : NO;
            if (_time == 0) {
                [self.dispoable dispose];
            }
        }];
    }];

点击运行,结果没有问题,拖动textview的滚动条,计时器依然在倒计时。
代码在一处就实现了所有功能。下面看看实现原理。
先说一下下面这行代码的作用吧

    [[_btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        NSLog(@"Hello RAC!");
    }];

这个代码的作用是创建了一个信号,并且订阅了这个信号。等按钮点击了,这个block就会执行。具体的代码我们下一篇文章再分析吧!这里就知道我们点击按钮这个block会执行就OK。进入block里面,主要就是又创建了一个信号,下面我们通过代码看都实现了什么功能。

[[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
            _time --;
            NSString * title = _time > 0 ? [NSString stringWithFormat:@"请等待 %d 秒后重试",_time] : @"发送验证码";
            [self.btn setTitle:title forState:UIControlStateNormal | UIControlStateDisabled];
            self.btn.enabled = (_time==0)? YES : NO;
            if (_time == 0) {
                [self.dispoable dispose];
            }
        }];

继续深入interval:onScheduler:方法的定义之前,看一下这个方法的第二个参数,生成方法如下,它是RACTargetQueueScheduler类型的单例对象。

+ (RACScheduler *)mainThreadScheduler {
    static dispatch_once_t onceToken;
    static RACScheduler *mainThreadScheduler;
    dispatch_once(&onceToken, ^{
        mainThreadScheduler = [[RACTargetQueueScheduler alloc] initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.mainThreadScheduler" targetQueue:dispatch_get_main_queue()];
    });
    
    return mainThreadScheduler;
}

然后进入interval:onScheduler:方法

+ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler {
    return [[RACSignal interval:interval onScheduler:scheduler withLeeway:0.0] setNameWithFormat:@"+interval: %f onScheduler: %@", (double)interval, scheduler];
}

这个方法就是简单的调用了下面的方法。下面的方法就是创建了一个信号,最终调用的是createSignal方法。当然这个方法需要传递一个block,这个之前关于RAC的文章里有说明,这个block会在subscribeNext:方法调用的时候执行。

+ (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler withLeeway:(NSTimeInterval)leeway {
    NSCParameterAssert(scheduler != nil);
    NSCParameterAssert(scheduler != RACScheduler.immediateScheduler);

    return [[RACSignal createSignal:^(id subscriber) {
        return [scheduler after:[NSDate dateWithTimeIntervalSinceNow:interval] repeatingEvery:interval withLeeway:leeway schedule:^{
            [subscriber sendNext:[NSDate date]];
        }];
    }] setNameWithFormat:@"+interval: %f onScheduler: %@ withLeeway: %f", (double)interval, scheduler, (double)leeway];
}

继续进入方法after: repeatingEvery: withLeeway: schedule:,这个方法最后一个参数是一个block,用来发送信号[subscriber sendNext:[NSDate date]];,只有发送了信号subscribeNext:后面的block才会执行。这个方法的返回值是一个RACDisposable对象,这个前面的文章里也说了,就是createSignal:传递的block参数的返回值。在这个方法里,其实就是启动了一个GCD的定时器。定时器执行block,这个block就包含一个语句
[subscriber sendNext:[NSDate date]];就是发送时间事件。这样就触发了subscribeNext:后面的block。

- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block {
    NSCParameterAssert(date != nil);
    NSCParameterAssert(interval > 0.0 && interval < INT64_MAX / NSEC_PER_SEC);
    NSCParameterAssert(leeway >= 0.0 && leeway < INT64_MAX / NSEC_PER_SEC);
    NSCParameterAssert(block != NULL);

    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);
    }];
}

上面的方法启动了一个定时器,并且返回了一个RACDisposable对象。一订阅这个定时器就启动,通过[subscriber sendNext:[NSDate date]];定时发送消息执行subscribeNext后面的代码。

参考文章:
https://www.jianshu.com/p/064f81e28a28

你可能感兴趣的:(RAC实战3---定时器)