iOS进阶-谈谈定时器
目录
- iOS提供定时器API
- 定时器开发中的坑
一、 iOS提供定时器API
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
二、定时器开发中的坑
2.1、必须***又一个runloop
系统框架提供了几种创建NSTimer的方法,其中以scheduled开头的方***自动把timer加入当前RunLoop,到了设定时间就会触发selector方法,而没有scheduled开头的方法则需要手动添加timer到一个RunLoop中才会有效。
程序启动时,会默认启动主线程的RunLoop并在程序运行期内有效,所以把timer放入主线程时不需要启动RunLoop,但现实开发中主线程更多的是处理UI事物,把耗时且耗能的操作放在子线程中,这就需要将子线程的RunLoop激活。
注意: 由于RunLoop自己主要有两种使用模式:NSDefaultRunLoopMode、NSEventTrackingRunLoopMode,一般会默认使用NSDefaultRunLoopMode,但是默认模式会出现当界面UI如果存在连续事件,定时器会自动进入NSEventTrackingRunLoopMode模式定时器将会被停止。一般我们都是用NSRunLoopCommonModes,它是前两者的集合;
2.2、内存泄漏
- (void)cancel{
[_timer invalidate];
_timer = nil;
}
- (void)dealloc{
[self cancel];
}
我们通常会在界面销毁的生命周期函数销毁,但是是没有效果的:我们分析下:
当我们当前一个定时器_timer,_timer对target、userInfo,存在一个强引用,target、userInfo的引用技术都会加一,一般target都是当前类的对象,那么因为_time一直对当前类有一个强的引用,导致它无法销毁,导致内存泄漏,所以以上的释放方式是不对的。
正确的方式如下:应该在界面消失的时候释放:
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[self cancel];
}
注意: 释放Time,执行 invalidate 需要在统一线程使用,不允许跨线程。