iOS中GCD定时器详解
作者:任淏
前言:CADisplayLink、NSTimer 不准时
CADisplayLink、NSTimer是基于RunLoop机制的,如果RunLoop的任务过于繁重,有可能会导致前两个定时器不准时。
举个例子:
加入我们创建了一个NSTimer定时器,每1秒钟做任务。那么,什么时候执行NSTimer呢?
是在RunLoop跑圈的过程中执行NSTimer定时器,而RunLoop跑完一圈执行的时间不固定,也就导致有可能1秒钟过去了,但是RunLoop还没有执行到定时器的任务,那么,这就造成定时器有可能不准时。
一、GCD 定时器
GCD是不依赖与RunLoop,是直接跟系统内核交互的。时间比较准确。
GCD 定时器简单的使用:
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"begin"); // 队列 dispatch_queue_t queue = dispatch_get_main_queue(); // 创建定时器 dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); // 设置时间 uint64_t start = 2.0; uint64_t interval = 1.0; dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0); // 设置回调 dispatch_source_set_event_handler(timer, ^{ NSLog(@"111"); }); // 启动定时器 dispatch_resume(timer); self.timer = timer; }
2022-07-05 17:42:46.674345+0800 Interview02-GCD定时器[13943:350556] begin
2022-07-05 17:42:48.675440+0800 Interview02-GCD定时器[13943:350556] 111
2022-07-05 17:42:49.675542+0800 Interview02-GCD定时器[13943:350556] 111
2022-07-05 17:42:50.675350+0800 Interview02-GCD定时器[13943:350556] 111
2022-07-05 17:42:51.674523+0800 Interview02-GCD定时器[13943:350556] 111
二、GCD 定时器的实现方案
第一步:封装
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface RHGCDTimer : NSObject + (NSString *)timerWithBlockTask:(void(^)(void))blockTask star:(float)star interval:(float)interval repeat:(BOOL)repeat async:(BOOL)async; + (void)cancelTask:(NSString *)name; @end NS_ASSUME_NONNULL_END
#import "RHGCDTimer.h" static NSMutableDictionary *timersDict; static dispatch_semaphore_t semaphore; @implementation RHGCDTimer + (void)initialize { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ timersDict = [NSMutableDictionary dictionary]; semaphore = dispatch_semaphore_create(1);//创建一个信号量,只允许一个线程操作 }); } + (NSString *)timerWithBlockTask:(void (^)(void))blockTask star:(float)star interval:(float)interval repeat:(BOOL)repeat async:(BOOL)async { if (!blockTask || star<0 || (repeat && interval <= 0)) return nil; //创建队列,队列决定到时候任务是在哪个线程执行 dispatch_queue_t queue = async ? dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL) : dispatch_get_main_queue(); //创建一个定时器 dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); /** dispatch_source_set_timer 上面的定时器 dispatch_time_t start 开始时间 (typedef uint64_t dispatch_time_t;) uint64_t interval 间隔 uint64_t leeway 误差一般写0 */ dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, star * NSEC_PER_SEC), interval *NSEC_PER_SEC, 0); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//信号量 //定时器唯一标识 static int i = 0; NSString *name = [NSString stringWithFormat:@"%d", i++]; //放进字典,就会产生强引用 timersDict[name] = timer; dispatch_semaphore_signal(semaphore); //设置回调 dispatch_source_set_event_handler(timer, ^{ blockTask(); if (!repeat) {//如果非重复执行 [self cancelTask:name];//取消定时器 } }); //启动定时器 dispatch_resume(timer); //GCD不需要销毁 return name; } + (void)cancelTask:(NSString *)name { if (name.length == 0) return; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_source_t timer = timersDict[name]; if (!timer) return; dispatch_source_cancel(timer); [timersDict removeObjectForKey:name]; dispatch_semaphore_signal(semaphore); } @end
第二步:使用
#import "ViewController.h" #import "RHGCDTimer.h" @interface ViewController () @property (copy, nonatomic) NSString *task; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.task = [RHGCDTimer timerWithBlockTask:^{ NSLog(@"执行任务---%@", [NSThread currentThread]); } star:2.0 interval:1.0 repeat:YES async:YES]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [RHGCDTimer cancelTask:self.task]; } @end
第三步:测试验证
2022-07-05 17:31:41.375918+0800 Interview02-GCD定时器[13519:337316] 执行任务---<_NSMainThread: 0x600002448880>{number = 1, name = main}
2022-07-05 17:31:42.375935+0800 Interview02-GCD定时器[13519:337316] 执行任务---<_NSMainThread: 0x600002448880>{number = 1, name = main}
2022-07-05 17:31:43.375871+0800 Interview02-GCD定时器[13519:337316] 执行任务---<_NSMainThread: 0x600002448880>{number = 1, name = main}
到此这篇关于iOS中GCD定时器详解的文章就介绍到这了,更多相关iOS GCD定时器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!