一、NSTimer 运行条件
在一个NSRunloop 中的某个模式中运行,所在的runloop必须是运行的。
简单介绍Runloop:
一个线程对应一个runloop, 主线程runloop默认运行的,新建子线程runloop是未运行的,需要手动运行。
一个runloop包含的5个Mode:
- kCFRynLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响
- kCFRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode
- UIInitializationRunLoopMode:在刚启动App时进入的第一个Mode,启动完成后不再使用
- GSEventReceiveRunLoopMode:接受系统事件的内部Mode,通常用不到
如果Nstimer在当前runloop的DefaultMode,则当前runloop的触摸滑动会使其暂时停止运行,滑动结束后会继续运行。
二、NSTimer便利创建
当前线程, 自动加入当前runloop, 并以默认模式 NSDefaultRunLoopModel 运行.
runloop强引用timer, self强引用timer, timer强引用target: 而target通常是 self, 所以会存在强引用循环.
在销毁self之前,必须先手动的调用 invalidate方法将timer停止.虽然timer不会销毁,但此方法会将timer持有的target置nil.就不会强引用了.
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(timerFuction:) userInfo:@{@"a" : @"aaa"} repeats:YES];
// timer调用的方法,注意对UI的操作如果不是主线程,需要切换到主线程
- (void)timerFuction:(NSTimer *)timer {
NSDictionary *dic = (NSDictionary *)timer.userInfo;
self.lab.text = [NSString stringWithFormat:@"%@:%@", [dic valueForKey:@"a"] ,[NSDate date]];
}
iOS10 开始的API
当前线程, 自动加入当前runloop, 并以默认模式 NSDefaultRunLoopModel 运行.
runloop强引用timer, self强引用timer, timer强引用block, 所以block内要弱引self,否则引用循环.
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 repeats:YES block:^(NSTimer * _Nonnull timer) {
weakSelf.lab.text = [NSString stringWithFormat:@"%@", [NSDate date]];
}];
三、NSTimer指定 Runloop 和 RunloopMode 创建
只创建了timer, 未添加到runloop的话不会运行. 但此时target如果是self的话,已经构成了强引用循环的犯罪事实.
运行条件: 将其加入到指定的runloop, 指定的runloop的模式,。
如果runloop的线程不是主线程, 还必须启动runloop才会运行.
self.timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:@selector(timerFuction:) userInfo:@{@"a" : @"aaa"} repeats:YES];
// 加入到当前runloop的默认模式. 此处为主线程,runloop是已经启动的
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
// 如果是新创建的子线程,runloop是未启动的,就要手动启动runloop
dispatch_queue_t queue = dispatch_queue_create("somequeue", DISPATCH_CURRENT_QUEUE_LABEL);
dispatch_async(queue, ^{
self.timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:@selector(timerFuction:) userInfo:@{@"a" : @"aaa"} repeats:YES];
// 子线程的 runloop, 需要手动开启,因为是子线程的runloop,所以即使主线程在滚动,此时的timer也不会暂停的
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addTimer:self.timer forMode:NSDefaultRunLoopMode];
[runloop run];
});
// timer 调用的方法
- (void)timerFuction:(NSTimer *)timer {
NSDictionary *dic = (NSDictionary *)timer.userInfo;
// 子线程trimer的方法,要回到主线程刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.lab.text = [NSString stringWithFormat:@"%@:%@", [dic valueForKey:@"a"] ,[NSDate date]];
});
}
Bloc的形式类似。