整理NSTimer使用及注意事项
Posted 锦夏ing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了整理NSTimer使用及注意事项相关的知识,希望对你有一定的参考价值。
1、NSTimer的创建
// 创建一个定时器,但是么有添加到运行循环,我们需要在创建定时器后手动的调用 NSRunLoop 对象的 addTimer:forMode: 方法。
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
// 创建一个timer并把它指定到一个默认的runloop模式中,并且在 TimeInterval时间后 启动定时器
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
// 默认的初始化方法,(创建定时器后,手动添加到 运行循环,并且手动触发才会启动定时器) - (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(nullable id)ui repeats:(BOOL)rep NS_DESIGNATED_INITIALIZER;
示例:
SvTestObject *testObject4 = [[SvTestObject alloc] init];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:1 target:testObject4 selector:@selector(timerAction:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
2、NSTImer使用
// 启动 Timer 触发Target的方法调用但是并不会改变Timer的时间设置。 即 time没有到达到,Timer会立即启动调用方法且没有改变时间设置,当时间 time 到了的时候,Timer还是会调用方法。 - (void)fire;
// 启动定时器
timer.fireDate = [NSDate distantPast];
//停止定时器
timer.fireDate = [NSDate distantFuture];
// 开启
[time setFireDate:[NSDate distanPast]]
//关闭
[time setFireDate:[NSDate distantFunture]]
//继续。
[timer setFireDate:[NSDate date]];
// 停止 Timer ---> 唯一的方法将定时器从循环池中移除 - (void)invalidate;
3、NSTimer注意事项
3.1、NSTimer加到了RunLoop中但未能触发事件
原因主要有以下两个:
3.1.1、runloop是否运行
每一个线程都有它自己的runloop,程序的主线程会自动的使runloop生效,但对于我们自己新建的线程,它的runloop是不会自己运行起来,当我们需要使用它的runloop时,就得自己启动。
- (void)applicationDidBecomeActive:(UIApplication *)application { // NSThread 创建一个子线程 [NSThread detachNewThreadSelector:@selector(testTimerSheduleToRunloop1) toTarget:self withObject:nil]; } // 测试把timer加到不运行的runloop上的情况 - (void)testTimerSheduleToRunloop1 { NSLog(@"Test timer shedult to a non-running runloop"); SvTestObject *testObject4 = [[SvTestObject alloc] init]; NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:1 target:testObject4 selector:@selector(timerAction:) userInfo:nil repeats:NO]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; // 打开下面一行输出runloop的内容就可以看出,timer却是已经被添加进去 //NSLog(@"the thread‘s runloop: %@", [NSRunLoop currentRunLoop]); // 打开下面一行, 该线程的runloop就会运行起来,timer才会起作用 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]]; NSLog(@"invoke release to testObject4"); } - (void)applicationWillResignActive:(UIApplication *)application { NSLog(@"SvTimerSample Will resign Avtive!"); }
3.1.2、mode是否正确
run loop在运行时一般有两个mode,一个defaultmode,一个trackingmode。同一线程的runloop在运行的时候,任意时刻只能处于一种mode。所以只能当程序处于这种mode的时候,timer才能得到触发事件的机会。
正常情况下run loop使用defaultmode,scheduled生成的timer会默认添加到defaultmode中,当我们互动scrollview时,run loop切换到trackingmode运行,于是我们发现定时器失效了。为了使定时器在我们滑动scrollview时也能正常运行,我们需要确保defaultmode和trackingmode里都添加了我们生成的timer。如:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:_timeInterval target:self selector:@selector(addone) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
或者:
NSTimer *timer = [NSTimer timerWithTimeInterval:_timeInterval target:self selector:@selector(addone) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
或者
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:_timeInterval target:self selector:@selector(addone) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
综上: 要让timer生效,必须保证该线程的runloop已启动,而且其运行的runloopmode也要匹配。
3.2、
使用NSTimer时,timer会保持对target和userInfo参数的强引用。只有当调取了NSTimer的invalidate方法时,NSTimer才会释放target和userInfo。
生成timer的方法中如果repeats参数为NO,则定时器触发后会自动调取invalidate方法。如果repeats参数为YES,则需要程序员手动调取invalidate方法才能释放timer对target和userIfo的强引用。
在使用repeats参数为YES的定时器时,如果在使用完定时器时后没有调取invalidate方法,导致target和userInfo没有被释放,则可能会形成循环引用情况,从而影响内存释放。
//取消定时器
[timer invalidate]; // 将定时器从运行循环中移除
timer = nil; // 销毁定时器 ---》 这样可以避免控制器不死
以上是关于整理NSTimer使用及注意事项的主要内容,如果未能解决你的问题,请参考以下文章
在子线程中使用runloop,正确操作NSTimer计时的注意点 三种可选方法
AFHTTPSessionManager下载文件 及下载中 进度条处理,进度条处理需要特别注意,要加载NSRunLoop 中