zoukankan      html  css  js  c++  java
  • NSTimer深入理解

    NSTimer,即计时器,用于定时执行一些任务,一次或者多次。

    系统Foundation框架提供的最常用方法如下,创建一个NSTimer,并将它放到当前runloop的default mode中。

    + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti // 执行任务的时间间隔 
                          target:(id)aTarget // 执行任务的对象
                         selector:(SEL)aSelector // 执行任务的对象方法
                         userInfo:(nullable id)userInfo // 用于传一些参数
                         repeats:(BOOL)yesOrNo; // 是否重复执行
    

    1、怎么保证在未来某个时间点,要执行任务时,target还有效呢?target完全有可能被释放了呀。鉴于此,NSTimer会持有target对象,直到NSTimer invalidate。不同的是,一次性NSTimer会在执行完任务之后,会自动invalidate;而重复性NSTimer,则需要手动invalidate,否则会造成内存泄漏。   

    2、由于NSTimer会持有target对象,如果刚好target对象就是self,而NSTimer又是self的一个实例变量,就会引发循环引用:self->NSTimer->self。

    @interface TimerTest () {
        NSTimer *_timer; // self持有_timer
    }
    
    @end
    
    @implementation TimerTest
    
    - (id)init
    {
        if (self = [super init]) {
            _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(test) userInfo:nil repeats:YES]; // _timer持有self
        }
        return self;
    }
    
    - (void)dealloc
    {
        // 因为_timer持有self,所以self的引用计数>=1,也就执行不到dealloc。
        [_timer invalidate]; // 而执行不到dealloc,这句也就执行不到,_timer会一直持有self。这时候只能其他地方将_timer invalidate才行。
    }
    
    - (void)test
    {
    
    }
    
    @end

    为了解决这种循环引用问题,主要有两种方法:

    1)实现一个NSTimer的category,并提供block接口。

    @implementation NSTimer (Safe)
    
    + (NSTimer *)safeScheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                            repeats:(BOOL)repeats
                                                blk:(void(^)())blk {
        return [self scheduledTimerWithTimeInterval:interval
                                             target:self // target变成了NSTimer类对象,类对象由系统自动回收
                                           selector:@selector(_doBlock:) // NSTimer会把自己传过去
                                           userInfo:[blk copy] // 把block拷贝到堆上,避免栈block到用时已经被回收了
                                            repeats:repeats];
    }
    
    + (void)_doBlock:(NSTimer *)timer {
        void(^blk)() = timer.userInfo;
        if(blk) {
            blk();
        }
    }
    
    @end
    
    @interface TimerTest () {
        NSTimer *_timer; // self持有_timer
    }
     
    @end
     
    @implementation TimerTest
     
    - (id)init
    {
        if (self = [super init]) {
    __weak TimerTest *weakSelf = self; _timer
    = [NSTimer safeScheduledTimerWithTimeInterval:1 repeats:YES blk:^{
    __strong TimerTest *strongSelf = weakSelf;
    [strongSelf test];
    }]; } return self; } - (void)dealloc { [_timer invalidate]; } - (void)test { } @end 

    3、NSTimer必须放在runloop中才能生效。如下方法就只是创建一个NSTimer,而并没有把它放到runloop中。

    + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti 
                                target:(id)aTarget 
                              selector:(SEL)aSelector 
                              userInfo:(nullable id)userInfo 
                               repeats:(BOOL)yesOrNo;

    需要调用如下方法把它放到runloop中,才能生效。 

    - (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode;

    https://developer.apple.com/documentation/foundation/nstimer/2091889-scheduledtimerwithtimeinterval

  • 相关阅读:
    201521123036 《Java程序设计》第4周学习总结
    201521123036 《Java程序设计》第3周学习总结
    201521123075 《Java程序设计》第12周学习总结
    201521123075 《Java程序设计》第11周学习总结
    201521123075 《Java程序设计》第10周学习总结
    201521123075 《Java程序设计》第9周学习总结
    201521123075 《Java程序设计》第8周学习总结
    201521123075 《Java程序设计》第7周学习总结
    201521123075 《Java程序设计》第6周学习总结
    201521123075 《Java程序设计》第5周学习总结
  • 原文地址:https://www.cnblogs.com/yangwenhuan/p/9010856.html
Copyright © 2011-2022 走看看