NSTimer依赖于Runloop,如果Runloop的任务过于繁重,可能会导致NSTimer不准时。
而GCD定时器依赖于操作系统内核,更加准时。
GCD定时器的一些API:
//创建一个定时器
//type:DISPATCH_SOURCE_TYPE_TIMER//代表创建的是定时器
//handle:监视系统的句柄
//mask:需要哪些事件的掩码
//queue:放在哪个队列
dispatch_source_t timer = dispatch_source_create(dispatch_source_type_t type,
uintptr_t handle,
unsigned long mask,
dispatch_queue_t _Nullable queue);
//设置时间
//source:传入GCD创建的timer,
//start:开始时间,GCD对象的timer,使用dispatch_time(DISPATCH_TIME_NOW, start*NSEC_PER_SEC)创建,DISPATCH_TIME_NOW从现在开始 start*NSEC_PER_SEC纳秒后(GCD里以纳秒为单位)
//interval*NSEC_PER_SEC:interval*NSEC_PER_SEC个纳秒执行一次
//leeway:允许的误差。
dispatch_source_set_timer(dispatch_source_t source,
dispatch_time_t start,
uint64_t interval,
uint64_t leeway);
//设置回调的两个方法,一个是block,一个传入函数名
dispatch_source_set_event_handler(dispatch_source_t source,
dispatch_block_t _Nullable handler);
dispatch_source_set_event_handler_f(timer, timerFunc);
//启动
dispatch_resume(timer);
封装一个定时器,继承于NSObject的好处是可以防止别人调用别的其父类的别的接口
h文件:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface CS_Timer : NSObject
+ (NSString*)execTask:(void(^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async;
+ (NSString*)execTarget:(id)target selector:(SEL)selector start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async;
+ (void)cancelTask:(NSString*)TimerName;
@end
NS_ASSUME_NONNULL_END
m文件
#import "CS_Timer.h"
@implementation CS_Timer
static NSMutableDictionary *timers;
dispatch_semaphore_t semaphore;
+ (void)initialize{
static dispatch_once_t onceToken;
semaphore = dispatch_semaphore_create(1);
dispatch_once(&onceToken, ^{
timers = [[NSMutableDictionary alloc]init];
});
}
+ (NSString*)execTask:(void(^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async
{
if (!task || (interval<0 && repeats)|| start<= 0) return nil;
dispatch_queue_t queue = async?queue = dispatch_queue_create("asyncQueue", 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(timer, dispatch_time(DISPATCH_TIME_NOW, start*NSEC_PER_SEC), interval*NSEC_PER_SEC, 0);//最后一个参数,允许的纳秒误差。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSString *timerName = [NSString stringWithFormat:@"timerNo_%ld",timers.count];
timers[timerName] = timer;
dispatch_semaphore_signal(semaphore);
dispatch_source_set_event_handler(timer, ^{
task();
if(!repeats)
[self cancelTask:timerName];
});
dispatch_resume(timer);
return timerName;
}
+ (NSString*)execTarget:(id)target selector:(SEL)selector start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async
{
if (!target || !selector) return nil;
return [self execTask:^{
if ([target respondsToSelector:selector]) {
//消除警告
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[target performSelector:selector];
#pragma clang diagnostic pop
}
} start:start interval:interval repeats:repeats async:async];
}
+ (void)cancelTask:(NSString *)timerName{
if (timerName.length == 0) return;
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_source_t timer = timers[timerName];
if (timer) {
dispatch_source_cancel(timers[timerName]);
[timers removeObjectForKey:timerName];
}
dispatch_semaphore_signal(semaphore);
}
void timerFunc()
{
NSLog(@"GCD 回调方法");
}
@end