如何打破NSTimer与Target之间的Retain Cycle

NSTimer

NSTimerFoundation框架中一种很方便很有用的对象,可以:

  • 指定绝对的日期和时期,以便到时执行指定任务
  • 指定执行任务的相对延迟时间
  • 指定重复运行的任务

计时器要和run loop(运行循环)相关联,run loop到时候会触发任务。创建NSTimer时,可以将其预先安排在当前run loop中,也可以先创建好,然后手动调用加入run loop中,它才能正常触发任务。
系统方法:

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
                                     target:(id)target
                                   selector:(SEL)aSelector
                                   userInfo:(nullable id)userInfo
                                    repeats:(BOOL)repeats;

直接创建个实例对象,并将其加入当前run loop当中。由于计时器会保留目标对象target,所以反复执行任务通常会导致应用程序问题。也就是设置重复执行模式的那种计时器,很容易引入retain cycle(保留环)。

如何打破Retain Cycle

  • MSWeakTimer
  • NSTimer添加个handlerBlock

推荐MSWeakTimer

线程安全的Timer,不会对target进行retain操作,支持GCD Queue,NSTimer的替代品MSWeakTimer现已支持Pod,具体实现及用法请点击这里

下面主要谈谈第二种,如何自己实现来打破retain cycle
1.为NSTimer添加一个Category方法
NSTimer+WeakTimer.h

+ (NSTimer *)zx_scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
                                       repeats:(BOOL)repeats
                                  handlerBlock:(void(^)())handler;

NSTimer+WeakTimer.m

+ (NSTimer *)zx_scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
                                       repeats:(BOOL)repeats
                                  handlerBlock:(void(^)())handler
{
    return [self scheduledTimerWithTimeInterval:timeInterval
                                         target:self
                                       selector:@selector(handlerBlockInvoke:)
                                       userInfo:[handler copy]
                                        repeats:repeats];
}

+ (void)handlerBlockInvoke:(NSTimer *)timer
{
    void (^block)() = timer.userInfo;
    if (block) {
        block();
    }
}

2.如何使用这个Category方法
创建一个NSTimer

- (void)startPolling
{
    __weak typeof(self)weakSelf = self;
    self.timer = [NSTimer zx_scheduledTimerWithTimeInterval:5.0 repeats:YES handlerBlock:^void(void){
        __strong typeof(weakSelf)strongSelf = weakSelf;
        [strongSelf doPolling];
    }];
}

执行轮询任务slector

- (void)doPolling
{
    //Todo...;
}

销毁NSTimer对象

- (void)stopPolling
{
    [self.timer invalidate];
    self.timer = nil;
}

- (void)dealloc
{
    [self.timer invalidate];
}

计时器现在的targerNSTimer类对象。这段代码先是定义了个弱引用,令其指向self,然后block捕获这个引用,而不直接去捕获普通的self变量,也就是说self不会为计时器所保留。当block开始执行时,立刻生成strong强引用,以保证实例在执行期间持续存活,不被释放。

采用这种写法后,外界指向NSTimer的实例最后一个引用被释放后,则创建NSTimer的实例也随之被系统回收。


Refer: Effective Objective-C 2.0 Tips52

推荐阅读更多精彩内容

  • 由于文章长度限制,本文作为[译]线程编程指南(一)后续部分。 Run Loops Run loop是与线程相关的基...
    巧巧的二表哥阅读 987评论 0 5
  • 1.属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作...
    曾令伟阅读 843评论 0 10
  • 这是一篇对Run Loop开发文档《Threading Program Guide:Run Loops》的翻译,来...
    鸿雁长飞光不度阅读 3,379评论 3 29
  • 文/Jack_lin(简书作者)原文链接:http://www.jianshu.com/p/5d2163640e2...
    笔笔请求阅读 424评论 0 0
  • 参考资料 NSTimer深入理解RunLoop《编写高质量iOS与OS X代码的52个有效方法》中第52条:别忘了...
    水止云起阅读 478评论 0 0
  • 序: 做一张安静的诗签, 把漂亮的诗句, 记在精美的纸片上。 告诉自己, 也告诉气息相通的人, 草木静谧,虫鸟温和...
    凡夫俗子y阅读 705评论 0 3
  • 对于社会中对超过30岁但没有结婚的女性称之为“剩女”的说法,我不认同。李欣频老师在她的书里说过,人不是物品,哪有剩...
    可乐乐呵呵阅读 1,354评论 13 23
  • 感恩~坚持早起做营养早餐,我的厨艺并不好,但是我每天愿意花时间去准备营养的早餐,愿意不断地去学习尝试改进厨艺,我也...
    毛毛细雨mmxy阅读 166评论 0 0
  • 法国巴黎,塞纳河右岸,一座始建于十二世纪末的欧洲城堡矗立眼前。 这就是享誉世界的卢浮宫了。 这座800多岁的卢浮宫...
    关观阅读 2,144评论 1 4