NSTimer内存/循环引用问题整合

日常开发最常用的定时方法还是NSTimer,CADisplayLink跟GCDTimer写起来还是略麻烦。但是对于重复的任务有一个重要问题:何时停掉这个任务。而且NSTimer经常会出现循环引用的问题,因此整合记录下。

情况一:

Timer会强持有target,所以如果我们写个vc持有一个Timer,并且这个Timer初始化以vc为target,那就循环引用了


情况一
结果:
循环情况:

这种情况下即便我们pop了TestVC,testvc dealloc依然不会出现,控制台会无限制打印a

解决方法:

合适的情况下调用timer.invalidate()手动停掉timer,打破循环:



image.png

情况二:

那如果我们不持有这个timer,在某个方法里timer只是作为一个局部变量来一vc为target,这样testvc就不会抢引用timer,是否可以避免循环引用?


结果:

依然未释放。这是因为启动timer时需要把timer添加到当前runloop中,那当前runloop就会持有timer如果不调用timer的invalidate方法,这个timer就无法被runloop释放,timer所持有的target也无法被释放。(scheduledTimer方法相当于initTimer() + fire() + runloop.addTimer()三合一)


image.png
解决方法:

标记这个timer,合适的时候手动停掉

综合解决方案:在合适的时候停掉timer

思路:使用中间target:
image.png

使用一个InnerTarget,初始化Timer时,InnerTarget弱持有testvc,持有selector,弱持有testvc,timer强引用InnerTarget,在InnerTarget的selector中,使用weak target来调用selector方法,这样testvc就可以正常dealloc,当testvc释放时,InnerTarget可以感知,停止Timer即可实现自动释放timer:


image.png

再试试看:


image.png

image.png

正常释放
iOS10 新增的block初始化Timer的方法,解决了Timer强引用target的问题,但是无法解决Runloop持有Timer的问题,仍然需要手动释放timer

尝试:使用stopTarget+block来初始化Timer,使用思路类似于Target:

image.png
使用:
image.png

image.png

可以正常释放

缺点:

因为block会对内部对象进行一次引用计数+1所以如果block里出现了self,请一定要使用weak self。否则依然会出现循环引用情况:


image.png
引用图:
image.png

使用weak/unowned self可打破引用问题:


image.png
引用图:
image.png

你可能感兴趣的:(NSTimer内存/循环引用问题整合)