NSTimer顾名思义是定时器,在IOS开发中可以用于设定一定的时间间隔,让其执行一段代码,常用于小游戏中的计时功能或者倒计时功能实现。
一、 初始化定时器(常用的2种):
//(1)
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;//(常用)
//(2)
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;//(常用)
两种初始化方法的区别:
1、timerWithTimeInterval这个类方法(1)创建出来的对象如果不用 addTimer: forMode方法手动加入主循环池中,将不会循环执行。并且如果不手动调用fair,则定时器不会启动。
2、scheduledTimerWithTimeInterval这个方法(2)不需要手动调用fair,会自动执行,并且自动加入主循环池。
代码:
/** 参数介绍
* timerWithTimeInterval: 表示输入一个时间间隔对象,以秒为单位,一个>0的浮点类型的值,如果该值<0,系统会默认为0.1
* target: 事件代理,一般填self
* selector: 方法选择器,在时间间隔内,选择调用一个实例方法
* userInfo: timer携带的信息(可以有timer1.userInfo取出)
* repeats:是否循环 (YES-循环执行; NO-执行一次停止)
* 注意:当YES时,定时器会不断循环直至失效或被释放,当NO时,定时器会循环发送一次就失效。
*/
//初始化定时器timer1
NSTimer *timer1 = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(myFun:) userInfo:nil repeats:YES];
//将定时器timer1加入主循环池中
[[NSRunLoop mainRunLoop]addTimer:timer1 forMode:NSDefaultRunLoopMode];
//开始循环定时器timer1
[timer1 fire];
//初始化定时器timer2,并开始循环
NSTimer *timer2 = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(myFun:) userInfo:@"abc" repeats:YES]
注意:将计数器的repeats设置为YES的时候,self的引用计数会加1。因此可能会导致self(即viewController)不能release,所以,必须在合适的时间或者viewWillAppear的时候,将计数器timer停止,否则可能会导致内存泄露。
二、NSTimer是否准确
答案是否定的,而且有时候你会发现实际的触发时间跟你想象的差距还比较大。NSTimer不是一个实时系统,因此不管是一次性的还是周期性的timer的实际触发事件的时间可能都会跟我们预想的会有出入。差距的大小跟当前我们程序的执行情况有关系,比如可能程序是多线程的,而你的timer只是添加在某一个线程的runloop的某一种指定的runloopmode中,由于多线程通常都是分时执行的,而且每次执行的mode也可能随着实际情况发生变化。
因此 timer不是一种实时的机制,会存在延迟,而且延迟的程度跟当前线程的执行情况有关。
还有一种情况是,将NSTimer添加进主运行循环,当主线程有较多的任务的时候(比如有较多的UI操作),那么主线程可能就会阻塞NSTimer,进而执行UI操作。
这类的解决办法是:将定时器用NSRunLoopCommonModes模式添加到主运行循环中。
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(myLog:) userInfo:nil repeats:YES];
//将其添加到主运行循环,防止阻塞
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
三 关于内存释放
@property (nonatomic, strong) NSTimer timer;
-(void)dealloc{
NSLog(@"dealloc");
}
- (void)viewDidLoad {
[super viewDidLoad];
//初始化定时器
self.timer= [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(myLog:) userInfo:nil repeats:YES];
//添加一个返回按钮
UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 50, 50)];
btn.backgroundColor=[UIColor blueColor];
[btn addTarget:self action:@selector(btnClick) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
//监听按钮点击
-(void)btnClick{
//(要在控制器销毁之前将定时器移除,并清空),这样才能避免循环引用,防止内存泄露
if ([self.timer isValid]) {
[self.timer invalidate];
}
self.timer=nil;
[self dismissViewControllerAnimated:YES completion:nil];
}
四 分类
在使用定时器的时候,可以给定时器创建一个分类,方便调用
#import <Foundation/Foundation.h>
@interface NSTimer (Extension)
/** 暂停定时器 */
- (void)pauseTimer;
/** 重启定时器 */
- (void)resumeTimer;
/** 销毁定时器 */
- (void)removeTimer;
@end
@implementation NSTimer (Extension)
/** 暂停定时器 */
-(void)pauseTimer{
if (![self isValid]) return ;
[self setFireDate:[NSDate distantFuture]];
}
/** 重启定时器 */
-(void)resumeTimer{
if (![self isValid]) return;
[self setFireDate:[NSDate date]];
}
/** 销毁定时器 */
- (void)removeTimer{
if (![self isValid]) return;
[self invalidate];
self = nil;
}
@end
总结:在使用定时器的时候,需要注意的就是内存释放的问题,关键是在合适的地方销毁定时器,防止其与控制器循环引用。还有就是 timer不是一种实时的机制,会存在延迟。