





















/* NSTimer.h
Copyright (c) 1994-2015, Apple Inc. All rights reserved.
*/
#import <Foundation/NSObject.h>
#import <Foundation/NSDate.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSTimer : NSObject
1、参数repeats是指定是否循环执行,YES将循环,NO将只执行一次。
2、timerWithTimeInterval 这两个类方法创建出来的对象如果不用 addTimer: forMode方法手动加入主循环池中,将不会循环执行。
3、scheduledTimerWithTimeInterval 这两个方法会将定时器添加到当前的运行循环,运行循环的模式为默认模式。
4、init方法需要手动加入循环池,它会在设定的启动时间启动。
NSTimer 使用过程中的问题:
1、 内存释放问题
如果我们启动了一个定时器,在某个界面释放前,将这个定时器停止,甚至置为nil,都不能使这个界面释放,原因是系统的循环池中还保有这个对象。
( timer都会对它的target进行retain,我们需要小心对待这个target的生命周期问题,尤其是重复性的timer)
所以我们需要这样做:
-(void)dealloc{
NSLog(@"dealloc:%@",[self class]);
}
- (void)viewDidLoad {
[super viewDidLoad];
timer= [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(myLog:) userInfo:nil repeats:YES];
UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
btn.backgroundColor=[UIColor redColor];
[btn addTarget:self action:@selector(btn) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
-(void)btn{
if (timer.isValid) {
[timer invalidate];
便利构造器,它其实是做了两件事:
首先创建一个timer,然后将该timer添加到当前runloop的default mode中。
也就是这个便利方法给我们造成了只要创建了timer就可以生效的错觉,我们当然可以自己创建timer,然后手动的把它添加到指定runloop的指定mode中去。
NSTimer其实也是一种资源(事件),如果看过多线程变成指引文档的话,我们会发现所有的source(事件)如果要起作用,就得加到runloop中去。
同理timer这种资源要想起作用,那肯定也需要加到runloop中才会有效喽。
如果一个runloop里面不包含任何资源(事件)的话,运行该runloop时会处于一种休眠状态等待下一个事件。
没有将事件添加到运行循环中
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[self testTimerWithOutShedule];
}
- (void)testTimerWithOutShedule
{
NSLog(@"Test timer without shedult to runloop");
SvTestObject *testObject3 = [[SvTestObject alloc] init];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:1 target:testObject3 selector:@selector(timerAction:) userInfo:nil repeats:NO];
NSLog(@"invoke release to testObject3");
}
- (void)applicationWillResignActive:(UIApplication *)application
{
NSLog(@"SvTimerSample Will resign Avtive!");
}
我们新建了一个timer,为它指定了有效的target和selector,并指出了1秒后触发该消息,运行结果如下:

Snip20151212_4.png
消息永远也不会触发,原因很简单,我们没有将timer添加到runloop中。
综上: 必须得把timer添加到runloop中,它才会生效。
原因主要有以下两个:
1、runloop是否运行
每一个线程都有它自己的runloop,程序的主线程会自动的使runloop生效,但对于我们自己新建的线程,它的runloop是不会自己运行起来,当我们需要使用它的runloop时,就得自己启动。
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// NSThread 创建一个子线程
[NSThread detachNewThreadSelector:@selector(testTimerSheduleToRunloop1) toTarget:self withObject:nil];
}
我们新创建了一个线程,然后创建一个timer,并把它添加当该线程的runloop当中,但是运行结果如下:

Snip20151212_5.png
发现这个timer知道执行退出也没有触发我们指定的方法,如果我们把上面测试程序中“
//[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
这一行的注释去掉,则timer将会正确的掉用我们指定的方法。
2、mode是否正确
手动添加runloop的时候,可以看到有一个参数runloopMode,这个参数是干嘛的呢?
前面提到了要想timer生效,我们就得把它添加到指定runloop的指定mode中去,通常是主线程的defalut mode。但有时我们这样做了,却仍然发现timer还是没有触发事件。
这是因为timer添加的时候,我们需要指定一个mode,因为同一线程的runloop在运行的时候,任意时刻只能处于一种mode。所以只能当程序处于这种mode的时候,timer才能得到触发事件的机会。
综上: 要让timer生效,必须保证该线程的runloop已启动,而且其运行的runloopmode也要匹配。
//不重复,只调用一次。timer运行一次就会自动停止运行
myTimer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(scrollTimer) userInfo:nil repeats:NO];
需要重复调用, repeats参数改为 YES . ---> 定时器的模式是默认的
注意点:
[timer invalidate]是唯一的方法将定时器从循环池中移除
NSTimer可以精确到50-100毫秒.
NSTimeInterval类:是一个浮点数字,用来定义秒
NSTimer不是绝对准确的,而且中间耗时或阻塞错过下一个点,那么下一个点就pass过去了.
文/潇小溅(简书作者)
原文链接:http://www.jianshu.com/p/3ccdda0679c1
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。