实现倒计时的三种方式

在软件开发过程中,我们常常需要在某个时间后执行某个方法,或者是按照某个周期一直执行某个方法。在这个时候,我们就需要用到定时器。在iOS中有很多方法完成定时器的任务,例如 NSTimer、CADisplayLink 和 GCD都可以。

一、NSTimer

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:YES];
//销毁定时器
//[timer invalidate];
其特点是使用上面的创建方式,会自动把timer加入当前线程的运行循环中
NSTimer *timer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
其特点是如果使用以上面的方式创建定时器,就必须手动加入Runloop运行循环中,否则方法无法正常执行.
当我们是在子线程中创建定时器时,因为子线程的NSRunLoop默认是不开启的,所以我们还需要开启子线程的NSRunLoop

方式一

//创建子线程
    [NSThread detachNewThreadWithBlock:^{
        
        //在子线程中创建定时器
        NSTimer *t = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"%@",[NSThread currentThread]);
        }];
        //在当前子线程的RunLoop中添加定时器
        [[NSRunLoop currentRunLoop] addTimer:t forMode:NSRunLoopCommonModes];
        //开启当前子线程的运行循环
        NSRunLoop *r = [NSRunLoop currentRunLoop];
        [r run];
    }];

方式二

//创建子线程
        [NSThread detachNewThreadWithBlock:^{
            
            //在子线程中创建定时器
            [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
                NSLog(@"%@",[NSThread currentThread]);
                
            }];
            //开启子线程的运行循环
            NSRunLoop *r = [NSRunLoop currentRunLoop];
            [r run];
        }];

二、GCD

//设置倒计时时间
    //通过检验发现,方法调用后,timeout会先自动-1,所以如果从15秒开始倒计时timeout应该写16
    __block NSInteger timeout = 10;
    //获取全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //创建一个定时器,并将定时器的任务交给全局队列执行(并行,不会造成主线程阻塞)
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
    // 设置触发的间隔时间(设置定时器触发的时间间隔为1s)
    dispatch_source_set_timer(timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0);
    //设置定时器的触发事件
    dispatch_source_set_event_handler(timer, ^{
        
        if(timeout<=0){
            //倒计时结束,关闭定时器
            dispatch_source_cancel(timer);
            dispatch_async(dispatch_get_main_queue(), ^{
                //返回主线程刷新UI
            });
        }else{
            dispatch_async(dispatch_get_main_queue(), ^{
                //返回主线程刷新UI
                NSLog(@"%ld",timeout);
            });
        }
        timeout--;
    });
    dispatch_resume(timer);
//这里需要添加一个强引用,否则方法执行完timer对象就被释放了
self.timer = timer;

三、CADisplayLink

CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(logaction)];
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    displayLink.preferredFramesPerSecond = 1.0f;
    //销毁定时器
//    [displayLink invalidate];
//    displayLink = nil;
注意点是CADisplayLink是一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器。iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。
NSTimer的精确度就显得低了点,比如NSTimer的触发时间到的时候,runloop如果在阻塞状态,触发时间就会推迟到下一个runloop周期。并且 NSTimer新增了tolerance属性,让用户可以设置可以容忍的触发的时间的延迟范围。
在UI相关的动画或者显示内容使用 CADisplayLink比起用NSTimer的好处就是我们不需要在格外关心屏幕的刷新频率了,因为它本身就是跟屏幕刷新同步的。

你可能感兴趣的:(实现倒计时的三种方式)