美文网首页
iOS中如何正确释放GCD定时器(dispatch_source

iOS中如何正确释放GCD定时器(dispatch_source

作者: 口子窖 | 来源:发表于2021-09-01 14:48 被阅读0次

    iOS中如何正确释放GCD定时器(dispatch_source_t)

    一.现象

    通过云迹的崩溃,查询到崩溃在福袋的释放缓存的方法(clearLuckyBagInfo)中,然后就查看这个释放缓存的方法,发现这个方法中都是简单的释放,也有保护操作,就很纳闷儿为什么崩溃在了这里.

    - (void)clearLuckyBagInfo{
        self.LuckyBagDetailModel = nil;
        self.contentView = nil;
        self.residueTime = 0;
        if (self.countDownTimer) {
            dispatch_cancel(self.countDownTimer);
            self.countDownTimer = nil;
        }
        self.isSuspendTimer = NO;
        self.liveDto = nil;
        [self.winPriceArray removeAllObjects];
        self.winPriceArray = nil;
        if (self.tenSecondTimer) {
            dispatch_cancel(self.tenSecondTimer);
            self.tenSecondTimer = nil;
        }
        self.IsReciveLotteryMessage = NO;
        self.timeGone = 0;
    }
    

    二.查询和验证

    查看了上面的释放方法中,通过分析只有可能是定时器释放时引起的crash,然后就去查询dispatch_source_t的crash问题,结果发现了在dispatch_suspend 状态下,不能去释放定时器,如果此时释放定时器,就会crash.

    20210901143117503.png

    然后查看代码中为了定时器的时间退到后台再回来时的准确性,就在退到后台中将定时器挂起(dispatch_suspend ),回到前台是发现福袋未结束时就重新开启定时器(dispatch_resume),如果用户从后台回到前台时福袋已经结束了,这时就没调用dispatch_resume开启定时器,这时候释放定时器就会引起crash.然后就和测试验证了这个场景,确实会引起crash.

    // 程序进入后台
    - (void)resignActive:(NSNotification *)notification {
        if ([self.LuckyBagDetailModel.drawStatus isEqualToString:@"0"] && self.countDownTimer) {
            [self pauseCountDownTimer];//挂起定时器
            NSDate* currentdate = [NSDate dateWithTimeIntervalSinceNow:0];
            NSTimeInterval a=[currentdate timeIntervalSince1970];
            [[NSUserDefaults standardUserDefaults] setObject:@(a) forKey:snliveLuckyBagResignActiveDate];
        }
    }
    // 程序进入前台
    - (void)becomeActive:(NSNotification *)notification {
        if ([self.LuckyBagDetailModel.drawStatus isEqualToString:@"0"] && self.countDownTimer) {
            [self resumeCountDownTimer];//重启定时器
            NSDate* currentdate = [NSDate dateWithTimeIntervalSinceNow:0];
            NSTimeInterval a=[currentdate timeIntervalSince1970];
            NSTimeInterval resignDate = [[NSUserDefaults standardUserDefaults] doubleForKey:snliveLuckyBagResignActiveDate];
            self.timeGone = a - resignDate;
        }else{
            self.timeGone = 0;
        }
        
    }
    

    三.解决办法

    1.设置属性 isSuspendTimer 记录定时器timer是否 处于dispatch_suspend的状态。在定时器挂起时设置为YES,重新启动时设置为NO. 只要在 调用dealloc 时判断下,已经调用过 dispatch_suspend 则再调用下 dispatch_resume后再cancel,然后再释放timer。就不会引起崩溃了.

    
    //计时器是否挂起
    @property (nonatomic, assign) BOOL isSuspendTimer;
    
    - (void)pauseCountDownTimer{//挂起
        if(self.countDownTimer){
            self.isSuspendTimer = YES;
            dispatch_suspend(_countDownTimer);
        }
    }
    - (void)resumeCountDownTimer{//恢复
        if(self.countDownTimer){
            self.isSuspendTimer = NO;
            dispatch_resume(_countDownTimer);
        }
    }
    
    - (void)clearLuckyBagInfo{
        self.LuckyBagDetailModel = nil;
        self.contentView = nil;
        self.residueTime = 0;
        //释放定时器是先判断该定时器是否被挂起
        if (self.countDownTimer) {
            if (self.isSuspendTimer) {
                dispatch_resume(self.countDownTimer);
            }
            dispatch_cancel(self.countDownTimer);
            self.countDownTimer = nil;
        }
        self.isSuspendTimer = NO;
        self.liveDto = nil;
        [self.winPriceArray removeAllObjects];
        self.winPriceArray = nil;
        if (self.tenSecondTimer) {
            dispatch_cancel(self.tenSecondTimer);
            self.tenSecondTimer = nil;
        }
        self.IsReciveLotteryMessage = NO;
        self.timeGone = 0;
    }
    

    四.总结(dispatch_source_t)

    dispatch_suspend 状态下直接释放当前控制器或者释放定时器,会导致定时器崩溃。并且初始状态(未调用dispatch_resume)、挂起状态,都不能直接调用dispatch_source_cancel(timer),调用就会导致app闪退。

    相关文章

      网友评论

          本文标题:iOS中如何正确释放GCD定时器(dispatch_source

          本文链接:https://www.haomeiwen.com/subject/avixwltx.html