美文网首页
iOS gcdTimer VS NSTimer

iOS gcdTimer VS NSTimer

作者: 不成活不疯魔 | 来源:发表于2018-10-19 16:20 被阅读0次

    iOS中定时器的使用
    demo地址:
    https://github.com/liuxuleidota/LXLGCDTimer
    使用NSTimer,注意点:
    一、切换runloop时失效
    默认情况下NSTimer会加入到runloop defaultMode中,当界面上有scrollview滑动时,runloop会切换到trackingMode,此时NSTimer会暂停,如果要避免此情况,如下:

    - (IBAction)startNSTimer:(id)sender {
        _nsTimer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"%@ xxxx", NSTimerName);
        }];
        [[NSRunLoop mainRunLoop] addTimer:_nsTimer forMode:NSRunLoopCommonModes];
    }
    

    二、可能引起内存泄漏
    NSTimer与self相互持有,使用weakSelf解除循环引用:

        __weak typeof(self) weakSelf = self;
        NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
            [weakSelf doSth];
        }];
        [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    

    这样self可以正常走dealloc,但是,在self销毁后,timer并不会销毁!因为runloop仍然持有着timer
    所以需要在必要的位置,停止timer,比如viewController的viewWillDisapper中(但是viewWillDisapper调用时机有很多,只要有新的controller覆盖当前的controller,此方法就会调用,所以是否要在viewWillDisapper中调用,请根据自己的业务逻辑来处理)
    我的做法:

    //runloop虽然会强持有timer,但是是在把timer加入runloop之后,所以还是要强持有timer,arc会处理这个strong,没关系
    @property(nonatomic, strong) NSTimer *nsTimer;
    
    - (void)viewWillDisappear:(BOOL)animated{
        [super viewWillDisappear:animated];
        
        [self cancelNSTimer];
    }
    
    - (void)dealloc{
        [self cancelNSTimer];
        NSLog(@"%@ is dealloced, timer=%@", self, _nsTimer);
    }
    
    - (void)doSth{
        //doSth
        //when sth is done
        [self cancelNSTimer];
    }
    
    - (void)cancelNSTimer{
        if (!_nsTimer) {
            return;
        }
        
        //从runloop移除timer
        [_nsTimer invalidate];
        
        //将timer置为nil,如果没有此行,在dealloc中打印仍然可以看到timer不为空
        //至于在dealloc方法走完,self销毁后,timer是否被销毁,这里没有进一步测试,因为NSTimer不能子类化
        _nsTimer = nil;
    }
    

    三、不能跨线程操作NSTimer?
    此点有疑问,如下:

    - (void)cancelNSTimer{
        if (!_nsTimer) {
            return;
        }
        
        dispatch_queue_t queue = dispatch_queue_create("com.levi.queue", NULL);
        
        dispatch_sync(queue, ^{
            [_nsTimer invalidate];
            _nsTimer = nil;
        });
    }
    

    timer是在主线程中创建的,但是这里我在子线程中操作,同样有效,有知道的请解答下


    使用GCDTimer
    引入demo中LXLGCDTimer目录
    一、创建GCDTimer

    - (IBAction)startGCDTimer:(id)sender {
        [LXLGCDTimerManager.sharedInstance scheduleGCDTimerWithName:self.timerName interval:1 queue:dispatch_get_main_queue() repeats:YES option:CancelPreviousTimerAction action:^{
            //此方法中请使用weakSelf
            NSLog(@"%@ xxxx", GCDTimerName);
        }];
    }
    

    二、实现以下方法

    //也可自己实现timerName方法,达到同一界面添加多个定时器目的
    - (NSString *)timerName{
        return [NSString stringWithFormat:@"%@timer", NSStringFromClass(self.class)];
    }
    
    - (void)cancelGCDTimer{
        [LXLGCDTimerManager.sharedInstance cancelTimerWithName:self.timerName];
    }
    
    //适当位置取消定时器,不然也会出现像NSTimer一样的情况,controller已经销毁,但是定时器仍然在运行!
    - (void)viewWillDisappear:(BOOL)animated{
        [super viewWillDisappear:animated];
        
        [self cancelGCDTimer];
    }
    
    - (void)dealloc{
        [self cancelGCDTimer];
    }
    

    本来想写的标题是用gcdTimer替代NSTimer可以避免很多坑,但是写完发现其实NSTimer使用正确的话,代码量跟gcdTimer是一样的(还不包括引入的LXLGCDTimer!)
    总结:都可以用!

    相关文章

      网友评论

          本文标题:iOS gcdTimer VS NSTimer

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