http://blog.callmewhy.com/2015/07/06/weak-timer-in-ios/
今天了解到一个问题,关于nstimer的问题。一般来说,我们是这么创建一个timer的例子的:
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(printMessage:) userInfo:nil repeats:YES];
但是在实际运用过程中,我们一般会遇到这样子的情况,就是某一个页面存在一个轮播图,下方有一个tableview。当我们开始滑动tableview的时候,轮播图会停止运行,这个是为什么呢?在开启一个NSTimer实质上是在当前的runloop中注册了一个新的事件源,而当scrollView滚动的时候,当前的MainRunLoop是处于UITrackingRunLoopMode的模式下,在这个模式下,是不会处理NSDefaultRunLoopMode的消息(因为RunLoop Mode不一样),要想在scrollView滚动的同时也接受其它runloop的消息,我们需要改变两者之间的runloopmode.
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
通过这个方式更改runloopmode。
PS:在http异步通信的模块中也有可能碰到这样的问题,就是在向服务器异步获取图片数据通知主线程刷新tableView中的图片时,在tableView滚动没有停止或用户手指停留在屏幕上的时候,图片一直不会出来,这个是为什么呢?
针对NSTimer存在一个invalidate来把当前的timer对象注销,不过这个方法写在哪里呢?可不可以写在delloc方法里面呢?如果有人要说arc情况下没有delloc的话,请去小屋面壁吧。正常来说当一个对象销毁的时候,会调用delloc方法(arc情况下不需要调用[supper delloc])。如果我们把[_timer invalidate];
写在delloc里面会出现什么情况呢?发现根本不执行这个delloc方法。
原因是Timer添加到Runloop的时候,会被Runloop强引用:
Note in particular that 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.
然后Timer又会有一个对Target的强引用(也就是 self ):
Target is the object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to target until it (the timer) is invalidated.
也就是说NSTimer强引用了self,导致self一直不能被释放掉,所以也就走不到self的dealloc里。这样子的循环引用导致了timer释放不掉,self释放不掉。但是,其实不是所有的target都是强引用的,正常来说:
Control objects do not (and should not) retain their targets.
在考虑循环引用的时候,target的问题要针对其特定的点。
在invalidate方法的文档里还有这这样一段话:
You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.
NSTimer在哪个线程创建就要在哪个线程停止,否则会导致资源不能被正确的释放。看起来各种坑还不少。
如何解决这个问题呢?当我们遇到这种循环引用引起的问题时,打破某一个引用就可以了。具体实现就看别人的东西吧