iOS倒计时谈高精度的定时器dispatch_source_t

 

//常见的发送验证码倒计时

//倒计时

- (void)openCountdown:(UILabel *)label

{

    __block NSInteger time = 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);

    dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行

    dispatch_source_set_event_handler(_timer, ^{

        

        if(time <= 0){ //倒计时结束,关闭

            dispatch_source_cancel(_timer);

            dispatch_async(dispatch_get_main_queue(), ^{

                //设置按钮的样式

                label.text = @"00:00";    

                //[self.getCode setTitleColor:[UIColor redColor] forState:UIControlStateNormal;

            });

            

        }else{

            int seconds = time % 60;

            dispatch_async(dispatch_get_main_queue(), ^{

                

                label.text = OCSTR(@"%d",seconds);

//                //设置按钮显示读秒效果

//                [self.codeButton setTitle:[NSString stringWithFormat:@"    %.2ds   ", seconds] forState:UIControlStateNormal];

//                //[self.getCode setTitleColor:[UIColor redColor] forState:UIControlStateNormal];

//                self.codeButton.userInteractionEnabled = NO;

            });

            time--;

        }

    });

    dispatch_resume(_timer);

}

详谈:

NSTimer受runloop的影响,由于runloop需要处理很多任务,导致NSTimer的精度降低,在日常开发中,如果我们需要对定时器的精度要求很高的话,可以考虑dispatch_source_t去实现 。dispatch_source_t精度很高,系统自动触发,系统级别的源。下面是通过dispatch_source_t 创建 计时器的例子

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
 
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
 
    //开始时间
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC);
 
    //间隔时间
    uint64_t interval = 2.0 * NSEC_PER_SEC;
 
 
    dispatch_source_set_timer(self.timer, start, interval, 0);
 
    //设置回调
    dispatch_source_set_event_handler(self.timer, ^{
 
        NSLog(@"----self.timer---");
    });
 
 
    //启动timer
    dispatch_resume(self.timer);

GCD中除了主要的Dispatch Queue外,还有较次要的Dispatch Source。它是BSD系内核惯有功能kqueue的包装。
kqueue是在XUN内核中发生各种事件时,在应用程序编程方执行处理的技术。其CPU负荷非常小,尽量不占用资源。kqueue可以说是应用程序处理XUN内核中发生的各种事件的方法中最优秀的一种。
Dispatch Source的种类:
1、DISPATCH_SOURCE_TYPE_DATA_ADD 变量增加
2、DISPATCH_SOURCE_TYPE_DATA_OR 变量 OR
3、DISPATCH_SOURCE_TYPE_MACH_SEND MACH端口发送
4、DISPATCH_SOURCE_TYPE_MACH_RECV MACH端口接收
5、DISPATCH_SOURCE_TYPE_MEMORYPRESSURE 内存压力 (注:iOS8后可用)
6、DISPATCH_SOURCE_TYPE_PROC 检测到与进程相关的事件
7、DISPATCH_SOURCE_TYPE_READ 可读取文件映像
8、DISPATCH_SOURCE_TYPE_SIGNAL 接收信号
9、DISPATCH_SOURCE_TYPE_TIMER 定时器
10、DISPATCH_SOURCE_TYPE_VNODE 文件系统有变更
11、DISPATCH_SOURCE_TYPE_WRITE 可写入文件映像
Dispatch Source类型比较多,下面记录定时器功能的实现。
实现定时器功能非常简单,实现如下:

@interface ZBViewController ()
 
@property (nonatomic, strong) dispatch_source_t timer;
 
@end
- (void)viewDidLoad {
    [super viewDidLoad];
 
    //1.创建类型为 定时器类型的 Dispatch Source
    //1.1将定时器设置在主线程
    _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    //1.2设置定时器每一秒执行一次
    dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0);
    //1.3设置定时器执行的动作
    dispatch_source_set_event_handler(_timer, ^{
        //在这里实现业务逻辑...
        NSLog(@"来打我吖!!!");
    });
    //2.启动定时器
    dispatch_resume(_timer);
    // Do any additional setup after loading the view.
}
上述简单代码就已经实现了定时器功能, 注意:将定时器写成属性,是因为内存管理的原因,使用了dispatch_source_create方法,这种方法GCD是不会帮你管理内存的。之前我就忘记了,写完,高高兴兴的去运行---->What?已经启动了为什么定时器无效呢?现在想想...MDZZ!

dispatch_source_create(dispatch_source_type_t type,
 uintptr_t handle,
 unsigned long mask,
 dispatch_queue_t _Nullable queue)
第一个参数:dispatch_source_type_t type为设置GCD源方法的类型,前面已经列举过了。
第二个参数:uintptr_t handle Apple的API介绍说,暂时没有使用,传0即可。
第三个参数:unsigned long mask Apple的API介绍说,使用DISPATCH_TIMER_STRICT,会引起电量消耗加剧,毕竟要求精确时间,所以一般传0即可,视业务情况而定。
第四个参数:dispatch_queue_t _Nullable queue 队列,将定时器事件处理的Block提交到哪个队列之上。可以传Null,默认为全局队列。注意:当提交到全局队列的时候,时间处理的回调内,需要异步获取UI线程,更新UI...不过这好像是常识,又啰嗦了...

dispatch_source_set_timer(dispatch_source_t source,
 dispatch_time_t start,
 uint64_t interval,
 uint64_t leeway);
第一个参数:dispatch_source_t source......不用说了
第二个参数:dispatch_time_t start, 定时器开始时间,类型为 dispatch_time_t,其API的abstract标明可参照dispatch_time()和dispatch_walltime(),同为设置时间,但是后者为“钟表”时间,相对比较准确,所以选择使用后者。dispatch_walltime(const struct timespec *_Nullable when, int64_t delta),参数when可以为Null,默认为获取当前时间,参数delta为增量,即获取当前时间的基础上,增加X秒的时间为开始计时时间,此处传0即可。
第三个参数:uint64_t interval,定时器间隔时长,由业务需求而定。
第四个参数:uint64_t leeway, 允许误差,此处传0即可。

dispatch_source_set_event_handler(dispatch_source_t source,
 dispatch_block_t _Nullable handler)
第一个参数:dispatch_source_t source,...不用说了。
第二个参数:dispatch_block_t _Nullable handler,定时器执行的动作,需要处理的业务逻辑Block。

dispatch_resume(_timer)
定时器创建完成并不会运行,需要主动去触发,也就是调用上述方法。
调度源提供了源事件的处理回调,同时也提供了取消源事件处理的回调,使用非常方便。

dispatch_source_set_cancel_handler(dispatch_source_t source,
 dispatch_block_t _Nullable handler)
对,Dispatch Source是可以取消的。
Dispatch Source还有很多种类型,后续继续学习吧!
--------------------- 
作者:Crazy_bananas 
来源:CSDN 
原文:https://blog.csdn.net/wang_Bo_JustOne/article/details/76147050 
版权声明:本文为博主原创文章,转载请附上博文链接!

你可能感兴趣的:(iOS倒计时谈高精度的定时器dispatch_source_t)