ios内存泄漏-timer-循环引用引起内存泄漏?

在arc循环引用的案例中,其中有一种是timer导致的。

主要原因归结为

举例来说明

//TimerController.h

#import@interface TimerController : UIViewController

@property(nonatomic,strong) NSTimer* timer;

@end

//TimerController.m

-(void)viewDidLoad

{

     [super viewDidLoad];

     self.view.backgroundColor = [UIColor whiteColor];

     NSTimer* tmpTimer = [NSTimer scheduledTimerWithTimeInterval:1

                                                   target:self

                                                   selector:@selector(p_doSomeThing)

                                                   userInfo:nil

                                                   repeats:YES];

}

如果repeats = NO 是不会产生循环引用问题的。 因为自动释放池会释放tmpTimer中所分配的内存,这样保留环也就不存在了。

那么如果repeats = YES, 那么就会产生tmpTimer保留self,并且一直会被保留,导致控制器的dealloc方法没法被调用,导致内存泄漏。并且如果不执行invalidate定时器仍然继续执行。所以我觉得这里叫循环引用不是很贴切,因为tmpTimer所指向的那段内存没法被释放。

那么tmpTimer内存何时能被释放呢? timer调用invalidate的时候,为了能在适当的地方可以调用invalidate,那么我们用一个属性保存tmpTimer,即self.timer = tmpTimer;

这样我们明显看出几个问题,timer强引用了tmpTimer这块内存,tmpTimer又强引用了self,self又强引用了timer, 明显的循环引用产生了。 那么是不是将timer设成weak就解决问题了呢? 显然不是的,原因如我上面所说,timer引起内存泄漏可以概括为两个原因

1. timer对target的强引用

2.由于是repeats,timer所指向的那块内存是没办法让releasepool释放的。

所以仅仅用循环引用来描述是不恰当的。

invalidate的功效一方面是停止定时器的执行,另一方面让自动释放池知道tmpTimer那段内存可以进行回收了。那么解决问题的一种思路 只要在适当的地方调用invalidate即万事大吉了。控制器dealloc方法中肯定是不合适了,因为这个方法都没机会给系统吊起。

-(void) viewWillDisappear:(BOOL)animated

{

     [super viewWillDisappear:animated];

     [_timer invalidate];

     _timer = nil;

}

这个方法中调用是可行的,并且通过调试发现内存泄漏确实没有了。如果controller出栈时候这么处理也蛮合理的,那么如果是由该控制器跳转到另一控制器的情况呢,在这个地方调用就不合适了。

所以合理的逻辑应该是要在该对象销毁时才调用invalidate。

以上是我对timer引起内存泄漏的一些自己的分析,那么解决方案如何? 网上有很多,这里拿来其中一种方案

TimerWeakTarget.h
TimerWeakTarget.m


在控制器对象中使用

_timer = [TimerWeakTarget scheduledTimerWithTimeInterval:1 target:self selector:@selector(p_doSomeThing) userInfo:nil repeats:YES];即可

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容