为何调用invalidate后NSTimer的retainCount仍不为零?
问题解答
嗨,这其实是iOS开发里一个很常见的误区,核心原因在于**retainCount本身就是个不可靠的调试工具**,再结合NSTimer和RunLoop的交互逻辑,就出现了你看到的结果,下面具体拆解:
1. 初始计数为3的来源
当你调用scheduledTimerWithTimeInterval:这个类方法时,系统会悄悄做这几件事:
- 创建NSTimer实例,此时对象自身的初始retain计数为1
- 自动把这个timer注册到当前线程的RunLoop(默认是主线程RunLoop)的
NSDefaultRunLoopMode中,RunLoop会对timer做一次retain,计数+1 - 你的局部变量
timer持有了这个实例,又触发一次retain,计数+1
三者加起来,就得到了初始的3。
2. invalidate后计数变为2的原因
调用[timer invalidate]时,系统会执行这些操作:
- 把timer从RunLoop中移除,RunLoop释放对它的引用,计数减1(此时计数变为2)
- 但你的局部变量
timer还保持着对这个对象的强引用,所以计数不会再往下掉 - 另外,系统内部可能还有临时的autorelease引用(比如方法调用过程中产生的临时持有,还没被autorelease pool回收),这也会让计数维持在2而不是1。
关键提醒:别靠retainCount判断对象死活
苹果官方文档早就明确说了,retainCount的返回值根本没法准确反映对象的实际引用情况——系统框架内部有大量你看不到的隐式retain/release操作,比如RunLoop的持有、autorelease pool的临时引用等,这些都会干扰计数结果。
如果想确认timer会不会被释放,正确的做法是:
- 调用
invalidate后,立刻把timer变量置为nil,解除你的强引用 - 可以给NSTimer子类加个
dealloc方法(或者用关联对象监听),看对象会不会触发销毁逻辑,这比看retainCount靠谱多了
内容的提问来源于stack exchange,提问作者Yanzhang Fu
相关产品推荐
相关产品推荐

