前天面试问到NSTimer的循环引用,面试者说了一个他从网上找的方法:
给NSTimer
新建一个类方法,内部设置target
为NSTimer
类,然后通过block
回调供外部使用。
当时听到这个感觉有个问题,当前控制器确实会正常释放,那怎么确保定时器也正常释放了呢?他当时说没有考虑过这个问题。我想了一下自己使用定时器的时候,观察的是定时器方法,通过调用invalidate
方法,保证计时器方法不会再调用,就o了。那这样真的可以保证定时器释放吗?查看下API说明如下:
Timers work in conjunction with run loops. Run loops maintain strong references to their timers, so you don’t have to maintain your own strong reference to a timer after you have added it to a run loop.
这里说Run loops
会强引用Timers
,所以就算我们外部没有强引用Timers
,也能够正常使用它的计时功能。
Once scheduled on a run loop, the timer fires at the specified interval until it is invalidated. A nonrepeating timer invalidates itself immediately after it fires. However, for a repeating timer, you must invalidate the timer object yourself by calling its invalidate method. Calling this method requests the removal of the timer from the current run loop; as a result, you should always call the invalidate method from the same thread on which the timer was installed. Invalidating the timer immediately disables it so that it no longer affects the run loop. The run loop then removes the timer (and the strong reference it had to the timer), either just before the invalidate method returns or at some later point. Once invalidated, timer objects cannot be reused.
这里说明了对于一个重复计时的定时器,通过调用invalidate
方法,定时器会终止并从Run loops
中移除。也就是说我用完计时器然后调用invalidate
方法就不会出现内存泄露了。
其实上面那个面试者说的方法,网上也有,最终也是通过调用invalidate
解决的。
通过上面的分析,正确使用Timer
就不会造成内存泄露了,那么该如何证明呢。一般来说,如果对象释放,一定会走dealloc
方法。只要重写dealloc
方法就行了。但是这个类是系统类,我们没有办法直接在源文件中写,那该怎么写呢?
1. 子类化并重写dealloc
方法。
Subclassing Notes
Do not subclass NSTimer.
API中说不能够子类化,所以排除此方案。
2. 通过category
实现dealloc
方法。使用此方法,代码运行的时候会崩溃,应该是一直调用导致的卡死。
3. 通过instruments
检查对象内存的开辟量。打开instruments
并选择Allocations
。然后在下部的搜索框中输入timer
,就可以看到计时器对象当前的内存暂用量了。