美文网首页iOS ProcessIOS进阶ios进阶
警惕使用NSTimer时的循环引用

警惕使用NSTimer时的循环引用

作者: 黄龙辉 | 来源:发表于2015-10-13 23:05 被阅读4071次

    使用NSTimer可能会碰到循环引用的问题。特别是当类具有NSTimer类型的成员变量,并且需要反复执行计时任务时。例如

    _timer = [NSTimer scheduledTimerWithTimeInterval:5.0
                                              target:self
                                            selector:@selector(startCounting) userInfo:nil
                                             repeats:YES];
    

    类有一个成员变量_timer,给_timer设置的target为这个类本身。这样类保留_timer_timer又保留了这个类,就会出现循环引用的问题,最后导致类无法正确释放。

    解决这个问题的方式也很简单,当类的使用者能够确定不需要使用这个计时器时,就调用

    [_timer invalidate];
    _timer = nil;
    

    这样就打破了保留环,类也可以正确释放。但是,这种依赖于开发者手动调用方法,才能让内存正确释放的方式不是一个非常好的处理方式。所以需要另外一种解决方案。如下所示:

    @interface NSTimer (JQUsingBlock)
    + (NSTimer *)jq_scheduledTimerWithTimeInterval:(NSTimeInterval)ti
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats;
    @end
    
    @implementation NSTimer (JQUsingBlock)
    
    + (NSTimer *)jq_scheduledTimerWithTimeInterval:(NSTimeInterval)ti
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats{
    
        return [self scheduledTimerWithTimeInterval:ti
                                         target:self
                                       selector:@selector(jq_blockInvoke:)
                                       userInfo:[block copy]
                                        repeats:repeats];
    }
    
    + (void)jq_blockInvoke:(NSTimer *)timer{
    
        void(^block)() = timer.userInfo;
        if (block) {
            block();
        }
    }
    
    @end
    

    定义一个NSTimer的类别,在类别中定义一个类方法。类方法有一个类型为块的参数(定义的块位于栈上,为了防止块被释放,需要调用copy方法,将块移到堆上)。使用这个类别的方式如下:

    __weak ViewController *weakSelf = self;
    _timer = [NSTimer jq_scheduledTimerWithTimeInterval:5.0
                                                  block:^{
                                                      __strong ViewController *strongSelf = weakSelf;
                                                      [strongSelf startCounting];
                                                  }
                                                repeats:YES];
    

    使用这种方案就可以防止NSTimer对类的保留,从而打破了循环引用的产生。__strong ViewController *strongSelf = weakSelf主要是为了防止执行块的代码时,类被释放了。在类的dealloc方法中,记得调用[_timer invalidate]

    相关文章

      网友评论

      • AncientMing:可以,有思路。
      • 不必luo嗦:你好,请问这个方法+ (void)jq_blockInvoke:(NSTimer *)timer 为什么不是对象方法(-)
      • QiufengLeng:直接在timer创建的时候target使用weakSelf是否可行?
        9b8f9fc0c496:因为你仅仅传参而已, timer内部实现的时候会用 id obj = weakself 来强引用weakself, 所以这么做是徒劳的。
        iOS_aFei:同问,为什么不行?
      • a7642f69975b:__strong 那里 直接用self 不就可以了嘛?为什么要先weak 然后在strong呢?
        而block内strong不是又循环引用了嘛
        猴子的救兵520:strongSelf 是 block 内部声明的局部变量,block每次开始执行时才会对self引用+1,执行完毕后,strongSelf自动释放,又会对self引用-1。所以没有循环引用。
      • 8ae158dda3f2:好高端
      • 代码干货:不知道是否有用先收藏,日后在试
      • 十一岁的加重:学习了,收藏备用

      本文标题:警惕使用NSTimer时的循环引用

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