很详细的计时器文章 介绍了三种定时器方法
http://www.jianshu.com/p/c167ca4d1e7e
-
NSTimer
必须加入Runloop、
存在延迟、
UIScrollView滑动会暂停计时(若不想被UIScrollView滑动影响,需要将 timer 添加再到 UITrackingRunLoopMode 或 直接添加到NSRunLoopCommonModes 中) - GCD计时器 精度最好 ✨
-
CADisplayLink
刷新频率固定 (取决CPU的忙碌程度)、
屏幕刷新时调用 (CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。但如果调用的方法比较耗时,超过了屏幕刷新周期,就会导致跳过若干次回调调用机会)、
适合做界面渲染(CADisplayLink可以确保系统渲染每一帧的时候我们的方法都被调用,从而保证了动画的流畅性。)
后台常驻 的问题 看到了一篇文章
https://www.cnblogs.com/lyanet/archive/2013/03/26/2983079.html
还有另一篇
http://www.jianshu.com/p/d1ecc467faff
通过这两个
苹果文档说到任何应用都有3分钟的后台执行任务的时间 。。。跟苹果借点时间用用。。。不用配置info.plist 避免因为配置后台长期运行后,苹果审查出你滥用api(如果你配置了 项目中必须要有相关的功能)
第一篇提到了 应该在长期任务结束后,我们需要做一些事情进行清理:
结束所有的线程和定时器,不管他们是基础定时器还是GCD中创建的。
调用UIApplication的endBackgroundTask:方法来结束后台任务。
将任务标识设置为UIBackgroundTaskInvalid,标志我们的任务结束。
最后,当我们的应用回到前台,如果我们的后台任务还在执行中,我们需要确保我们在干掉它:
主要代码放在下边以防丢失
- 3分钟的后台执行任务
@property (nonatomic, unsafe_unretained) UIBackgroundTaskIdentifier backgroundTaskIdentifier;
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication* app = [UIApplication sharedApplication];
__weak typeof(self)weakSelf = self;
self.backgroundTaskIdentifier = [app beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{ if (weakSelf.backgroundTaskIdentifier != UIBackgroundTaskInvalid)
{
weakSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
});
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{ if (weakSelf.backgroundTaskIdentifier != UIBackgroundTaskInvalid)
{
weakSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
});
});
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid){
[self endBackgroundTask];
}
}
- (void) endBackgroundTask{
dispatch_queue_t mainQueue = dispatch_get_main_queue();
__weak typeof(self)weakSelf = self;
dispatch_async(mainQueue, ^(void) {
if (weakSelf != nil){
[[UIApplication sharedApplication] endBackgroundTask:weakSelf.backgroundTaskIdentifier];
weakSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
});
}
#pragma mark - 定时器(GCD)
- (void)creatTimer{
//设置倒计时时间
//__block 如果修饰指针时,指针相当于弱引用,指针对指向的对象不产生引用计数的影响
//通过检验发现,方法调用后,timeout会先自动-1,所以如果从30秒开始倒计时timeout应该写31
__block int timeOut = 31;
//获取全局队列
dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建一个定时器,并将定时器的任务交给全局队列执行(并行,不会造成主线程阻塞)
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, global);
//设置触发的间隔时间
// dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0); //每秒执行
//1.0 * NSEC_PER_SEC 代表设置定时器触发的时间间隔为1s
//0 * NSEC_PER_SEC 代表时间允许的误差是 0s
//block内部 如果对当前对象的强引用属性修改,应该使用 __weak typeof(self)weakSelf 修饰 避免循环引用
__weak typeof(self)weakSelf = self;
//设置定时器触发事件
dispatch_source_set_event_handler(timer, ^{
//倒计时 刷新UI,当倒计时为0时,结束倒计时
//1.每调用一次 时间-1
timeOut --;
//2.对timeOut 进行判断时间是停止倒计时 还是修改UI
if (timeOut <= 0) {
//停止倒计时时
//关闭定时器
dispatch_source_cancel(timer);
//主线程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
});
}else{
//主线程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
});
}
});
dispatch_resume(timer);
}