目前这个定时器实现了APP中全局倒计时的功能,切换页面和前后台,包括修改系统时间都不受影响。
- 1.修改系统时间导致的问题
dispatch_walltime(NULL, 1);
dispatch_walltime 是根据系统时钟的绝对时间,会受到系统时间的影响,如果修改系统时间,定时器会停止,所以我们使用dispatch_time
dispatch_time(DISPATCH_TIME_NOW, 0), 1.0 * NSEC_PER_SEC, 0)
- 2.切换前后台怎么倒计时继续
开始切换前后台时分别直接获取的系统时间([NSDate date]),后面发现如果修改系统时间之后也没法用,最后获取手机的开机时间得到时间戳解决,还有参考了其他的获取开机时间的方法,但是试了好像不太准
- (long)deviceLaunchTime {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec;
}
实现:
dispatch_source_t _timer;
long timeRemain;
long currentTimes;
+ (instancetype)sharedInstance {
static dispatch_once_t one;
static OrderTimerModel *timerModel;
dispatch_once(&one, ^{
if (timerModel == nil) {
timerModel = [[OrderTimerModel alloc] init];
}
});
return timerModel;
}
- (void)countDownWithDay:(NSInteger)day
withTotalTime:(long)totalTime
withHour:(NSInteger)hour
withMinute:(NSInteger)minute
withSecond:(NSInteger)second
completeBlock:(void (^)(NSInteger totalTime, NSInteger day, NSInteger hour, NSInteger minute, NSInteger second))completeBlock {
timeRemain = totalTime;
[self addObserver];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), 1.0 * NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
DDLogDebug(@"剩余时间%ld", timeRemain);
if (timeRemain <= 0) {
dispatch_source_cancel(_timer);
completeBlock(timeRemain, 0, 0, 0, 0);
} else {
NSInteger s = 1;
NSInteger m = s * 60;
NSInteger h = m * 60;
NSInteger d = h * 24;
NSInteger day = timeRemain / d; //天
NSInteger hour = (timeRemain - day * d) / h; //时考虑天1天23小时59分钟59秒
NSInteger minute = (timeRemain - day * d - hour * h) / m; //分
NSInteger second = (timeRemain - day * d - hour * h - minute * m) / s; //秒
if (completeBlock) {
completeBlock(timeRemain, day, hour, minute, second);
}
if (timeRemain <= 0) {
dispatch_source_cancel(_timer);
}
timeRemain--;
}
});
dispatch_resume(_timer);
}
- (void)cancelTimer {
if (_timer) {
dispatch_source_cancel(_timer);
_timer = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
}
- (void)addObserver {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppDidBackGround) name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppDidEnterForeground) name:UIApplicationWillEnterForegroundNotification
object:nil];
}
- (void)handleAppDidBackGround {
currentTimes = [self deviceLaunchTime];
dispatch_suspend(_timer);
}
- (void)handleAppDidEnterForeground {
long foreInterval = [self deviceLaunchTime];
if (currentTimes > 0) {
int differ = (int)(foreInterval - currentTimes);
timeRemain = (timeRemain >= 0) ? (timeRemain - differ) : timeRemain;
}
dispatch_resume(_timer);
}
- (long)deviceLaunchTime {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
网友评论