NSTimer潜在的内存泄露问题

2018-06-26  本文已影响48人  伦伦子_f7b3

对于NSTimer使用大家肯定都不陌生,贴一张使用代码

        看代码中SecondViewController有一个属性是testTimer,然后在[self test01]中给self.testTimer赋值,并进行了强引用,然后我们在看方法[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(doSomeThing) userInfo:nil repeats:YES],里面的target:所传参数为self,SecondViewController,即所以SecondViewController与self.testTimer之间存在循环引用,会造成内存泄漏。


解决办法:

        我们发现testTimer是用strong修饰的,想到之前我们解决delegate循环引用是,对delegate属性使用weak修饰来打破闭环解决循环引用问题,那么这里testTime也使用weak修饰是否能够解决呢?经过测试发现无法解决,那问题出现在哪里?

先看这两个创建NSTimer对象的方法:

1. + (NSTimer*)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;//生成timer但不执行

2. + (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;//生成timer并且纳入当前线程的run loop来执行,不需要用addTimer方法。

分析:

self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(doSomeThing) userInfo:nil repeats:YES];
    //相当于
    self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(doSomeThing) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];

      开启一个NSTimer实质上是在当前的runloop中注册了一个新的事件源, 所以会发现NSRunLoop也对timer进行了强引用。看下面示意图(假设testTimer属性用weak修饰)

所以timer引用计数永远也不会变成0,那么也就不会销毁,这样也就导致了对SecondViewController的引用也不会断开,那么SecondViewController也不会销毁了。

苹果给NSTimer类提供的有一个对象方法

- (void)invalidate; //销毁定时器的方法,详解:将timer从runloop上移除的唯一方法,同时timer也会移除target目标和userinfo参数对象的强引用关系

Stops the timer from ever firing again and requests its removal from its run loop.

This method is the only way to remove a timer from an NSRunLoop object. The NSRunLoop object removes its strong reference to the timer, either just before the invalidate method returns or at some later point.

If it was configured with target and user info objects, the receiver removes its strong references to those objects as well.

问题: 那么定时器timer属性是用strong修饰还是用weak修饰?

答:都可以,如果用strong修饰的话,调用- (void)invalidate方法销毁定时器之后,还有调用self.timer = nil来移除控制器对定时器的强引用,如果使用weak就不需要了。建议使用strong,strong的性能还是要更优于weak。

扩展:上面所说的调用- (void)invalidate方法来销毁定时器肯定是能够解决内存泄漏问题的,但对于这种方式还是可以优化,因为你无法保证开发者一定会在需要的地方调用了销毁的方法,那么有什么好的优化方案呢?接下来介绍:

上一篇下一篇

猜你喜欢

热点阅读