前言: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定時器內容請搜索html5模板網以前的文章希望大家以后多多支持html5模板網!