GCD定时器

说到定时器,比较常用的就是NSTimer,但是有时候NSTimer不能满足需求。不管是一次性的还是周期性的timer的实际触发事件的时间,都会与所加入的RunLoop和RunLoop Mode有关,如果此RunLoop正在执行一个连续性的运算,timer就会被延时出发。重复性的timer遇到这种情况,如果延迟超过了一个周期,则会在延时结束后立刻执行,并按照之前指定的周期继续执行。

除此之外,NSTimer在日常使用中还需注意可能存在的内存泄漏问题。而在GCD中,存在着一个纳秒级的系统源定时器。

GCD定时器

GCD定时器是一种特殊的分派源,基于分派队列。NSTimer基于运行循环,所以,尤其是在多线程中,GCD定时器要比NSTimer好用的多。另外,GCD定时器使用dispatch_block_t,而不是方法选择器。

1
2
3
4
5
@interface GCDTimer : NSObject
+ (GCDTimer *)repeatingTimerWithTimeInterval:(NSTimeInterval)seconds block:(dispatch_block_t)block;
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@interface GCDTimer()
@property (nonatomic, strong) dispatch_block_t block;
@property (nonatomic, strong) dispatch_source_t source;
@end
@implementation GCDTimer
+ (GCDTimer *)repeatingTimerWithTimeInterval:(NSTimeInterval)seconds block:(dispatch_block_t)block {
GCDTimer *timer = [[self alloc] init];
timer.block = block;
//首先,创建一个定时器分派源并绑定到主分派队列上,这使得定时器总是在主线程上触发。
timer.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
//设置触发的间隔时间。
uint64_t nsec = (uint64_t)(seconds * NSEC_PER_SEC);
//设置定时器
dispatch_source_set_timer(timer.source, dispatch_walltime(NULL, 0), nsec, 0);
//设置事件处理程序
dispatch_source_set_event_handler(timer.source, block);
//打开定时器(分派源通常都是需要配置的,所以它们创建的时候处于暂停状态,只有resume之后才会开始发送事件。)
dispatch_resume(timer.source);
return timer;
}
//挂起定时器
- (void)suspend {
if (self.source != nil) {
dispatch_suspend(self.source);
}
}
//销毁定时器
- (void)invalidate {
if (self.source != nil) {
dispatch_source_cancel(self.source);
self.source = nil;
}
self.block = nil;
}
- (void)dealloc {
//销毁时将定时器设置为无效
[self invalidate];
}
@end

注意事项:

//设置定时器
dispatch_source_set_timer(<dispatch_source_t  _Nonnull source>,
                          <dispatch_time_t start>, 
                          <uint64_t interval>,
                          <uint64_t leeway>)
  • 参数1 计时器源
  • 参数2 开始时间 当我们使用 dispatch_time 或者 DISPATCH_TIME_NOW 时,系统会使用默认时钟来进行计时。然而当系统休眠的时候,默认时钟是不走的,也就会导致计时器停止。使用 dispatch_walltime 可以让计时器按照真实时间间隔进行计时。
  • 参数3 间隔时间
  • 参数4 leeway 指的是一个期望的容忍时间,将它设置为 1 秒,意味着系统有可能在定时器时间到达的前 1 秒或者后 1 秒才真正触发定时器。在调用时推荐设置一个合理的 leeway 值。需要注意,就算指定 leeway 值为 0,系统也无法保证完全精确的触发时间,只是会尽可能满足这个需求。

最后

这里的设计的定时器还比较粗糙,日后在实际运用中会将完整的封装好的类呈现至此。立下Flag

请我吃颗糖,鼓励我继续创作!