iOS GCD定时器如何改写为长尾?

2026-04-11 08:411阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计817个文字,预计阅读时间需要4分钟。

iOS GCD定时器如何改写为长尾?

前言:CADisplayLink、NSTimer 不准时CADisplayLink、NSTimer 是基于 RunLoop 机制的,如果 RunLoop 的任务过于繁重,可能会导致这两个定时器不准确。

举例:添加了我们创建的一个 NSTimer 定时器

前言: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 定时器的实现方案

第一步:封装

iOS 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}

本文共计817个文字,预计阅读时间需要4分钟。

iOS GCD定时器如何改写为长尾?

前言:CADisplayLink、NSTimer 不准时CADisplayLink、NSTimer 是基于 RunLoop 机制的,如果 RunLoop 的任务过于繁重,可能会导致这两个定时器不准确。

举例:添加了我们创建的一个 NSTimer 定时器

前言: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 定时器的实现方案

第一步:封装

iOS 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}