GCD定时器

目前这个定时器实现了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];
}

你可能感兴趣的:(GCD定时器)