GCD定时器
注意点:下面代码中的timer是一个局部的变量,走出代码块,被销毁;所以定时器中的方法不会执行
/*
* @param dispatchQueue
* @param intervalInSeconds 间隔时间秒
* @param leewayInSeconds 允许的误差 如果传0 为没有误差
*
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"++++++++++++++++++++%@", [NSThread currentThread]);
});
dispatch_resume(timer);
对上面的解决办法:内部有一个强引用 引用着timer
@interface TestViewController ()
{
dispatch_source_t _mytimer;
}
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
_mytimer = timer;
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"++++++++++++++++++++%@", [NSThread currentThread]);
});
// 开启定时器
dispatch_resume(timer);
当我们需要停止定时器的时候,执行该方法:
注意:当在停止后不能再调用dispatch_resume
方法打开定时器,否则导致程序奔溃。
dispatch_source_cancel(_mytimer);
NSTimer
NSTimer定时器使用:
这里timer同样是一个局部变量,但是在函数体走完只有timer并没有销毁,所以定时器是可以正常工作,内部应该已经对timer有了一个强引用
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(test) userInfo:nil repeats:YES];
// 执行定时器
[timer fire];
// 定时器方法
- (void)test
{
NSLog(@"-------------");
}
同样的,当需要停止定时器的时候,调用该方法
- (void)invalidate;
如果需要再次开启定时器的时候,不可以在对该timer执行fire方法开启,而是需要充新创建一个新的定时器进行赋值
- (IBAction)switchAction:(UISwitch *)sender {
if (sender.isOn) { // 打开
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(test) userInfo:nil repeats:YES];
[self.timer fire];
}else{ // 关闭
[self.timer invalidate];
}
}
NSTimer定时器受NSRunloop影响:当timer被创建了以后,timer被添加到当前RunLoop的NSDefaultRunLoopMode
模式。而如果当前线程就是主线程,也就是UI线程时,某些UI事件,比如UIScrollView的拖动操作,会将Run Loop切换成NSEventTrackingRunLoopMode
模式,在这个过程中,默认的NSDefaultRunLoopMode
模式中注册的事件是不会被执行的
为了设置一个不被UI干扰的Timer,我们需要手动创建一个Timer,然后使用NSRunLoop的addTimer:forMode:方法来把Timer按照指定模式加入到Run Loop中。这里使用的模式是:NSRunLoopCommonModes,这个模式等效于NSDefaultRunLoopMode和NSEventTrackingRunLoopMode的结合
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(test) userInfo:nil repeats:YES];
// 执行定时器
[timer fire];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:UITrackingRunLoopMode];
NSRunLoop
NSRunLoop的特点
- 每条线程都有唯一的一个与之对应的RunLoop对象
- 主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
- RunLoop在第一次获取时创建,在线程结束时销毁
NSRunLoop的模式
如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入
RunLoop只能选择一个Mode启动,如果当前Mode中没有任何Source(Sources0、Sources1)、Timer,那么就直接退出RunLoop
系统默认注册了5个Mode:
kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode