NSTimer的用法
初始化方法 五个
关于这五个初始化方法我们分为三大类
第一类
+ (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;
根据指定参数创建一个NSTimer定时器,但是并没有加入到RunLoop循环中,需要我们手动加入到主循环池中[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode
,这是和第二类方法的主要区别
第二类
+ (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;
这两个方法同样依据指定参数创建一个定时器NSTimer,并自动加入到当前的RunLoop循环中,我们不需要手动加入,对比第一类方法
第三类
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(nullable id)ui repeats:(BOOL)rep
这个方法为NSTimer的指定构造方法,我们一般不经常用,供前面四个方法调用。当使用这个方法创建NSTimer实例的时候,需要我们手动加入到主循环池中[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode
。
方法参数
1.(NSTimeInterval)ti 定时器的触发时间间隔,如果这个参数<0或者=0,则默认为0.1毫秒
2.(NSInvocation *)invocation NSInvocation对象,构造定时器的回调对象。
3.(BOOL)yesOrNo 是否循环执行,YES循环,NO只执行一次
4.(id)aTarget 定时器回调时发送消息的执行对象,该对象执行(SEL)s方法
5.(SEL)aSelector 定时器回调时所执行的方法
6.(nullable id)userInfo 传递给定时器的参数信息
7.(NSDate *)date 定时器触发的时间
成员方法
- (void)fire;
立即触发此定时器,但并不影响之前设置的定时器时间间隔。如果定时器没有设置循环触发,那么执行此函数后,定时器被自动销毁,以后不会触发了。
- (void)invalidate;
停止定时器,并从循环池中移除这个定时器,从文档中可以看出,这是唯一的方法将定时器从循环池中移除。
成员变量
@property (copy) NSDate *fireDate;
设置定时器的启动时间,常用来管理定时器的启动和停止
//启动定时器
timer.fireDate = [NSDate distantPast];
或
[timer setFireDate:[NSDate distantPast]];
>//停止定时器
>```
timer.fireDate = [NSDate distantFuture];
或
[timer setFireDate:[NSDate distantFuture]];
//继续
timer.fireDate = [NSDate date];
或
[timer setFireDate:[NSDate date]];
@property (readonly) NSTimeInterval timeInterval;
只读属性,获取定时器调用的间隔时间
@property NSTimeInterval tolerance
设置定时器的误差范围
@property (readonly, getter=isValid) BOOL valid;
定时器是否有效
@property (nullable, readonly, retain) id userInfo;
获取传入定时器的参数信息
###使用NSTimer的注意事项
#####循环引用导致的内存泄漏
当我们创建NSTimer的时候,NSTimer会对传入的invocation、target这些参数强引用,直到调用```invalidate```方法,才释放这些对象。这就使得我们在使用NSTimer对象的时候要格外小心。
例如,如果我们在```viewDidLoad```或者其他的方法中创建了NSTimer实例,但是我们要在```dealloc```中销毁,此时就会出现循环应用。因为,不调用```invalidate```方法,永远进不了```dealloc```方法;而不进入```dealloc```方法,则永远也不会调用```invalidate```方法。这个时候就会出现内存泄漏。
避免这种情况的最简单的方法就是在```viewDidDisappear```或```viewWillDisappear```方法中调用```invalidate```方法
稍微麻烦一点就是自己对NSTimer进行封装.
#####添加到RunLoop
NSTimer必须加入到RunLoop循环池中才会起作用,NSTimer其实也是一种事件(资源),如果NSTimer这种资源要起作用的话,就必须加入到RunLoop循环中。
由前面我们可以了解到,通过初始化方法
- (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;
会将timer自动加入到当前的RunLoop中,如果此时所在的线程不是主线程,则需要我们手动启动当前线程的RunLoop```[[NSRunLoop currentRunLoop] run]```。
我们可以在UITableViewCell中这样使用
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(handleTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run]; //手动启动当前线程的RunLoop
})
######RunLoop模式切换
有这么一种情况,我们在UITableview或者ScrollView中使用NSTimer的时候,当UITableview或者ScrollView滚动的时候,NSTimer停止了。
比如使用这种方法在主线程中创建NSTimer
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(handleTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
我们将NSTimer添加到RunLoop的默认模式forMode:NSDefaultRunLoopMode,而当我们滚动ScrollView的时候RunLoop将会切换到UITrackingRunLoopMode,所以这个时候NSTimer就停止工作了,滚动完成之后,继续执行,这种情况我们可以这样处理:
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(handleTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; //将NSTimer放在forMode:NSRunLoopCommonModes模式下,在任何情况下都能够正常工作