为什么要在非主线程创建NSTimer
- 将 timer 添加到主线程的Runloop里面本身会增加线程负荷
- 如果主线程因为某些原因阻塞卡顿了,timer 定时任务触发的时间精度肯定也会受到影响
- 有些定时任务不是UI相关的,本来就没必要在主线程执行,给主线程增加不必要的负担。当然也可以在定时任务执行时,手动将任务指派到非主线程上,但这也是有额外开销的。
NSTimer
的重要特性
-
NSTimer
上的定时任务是在创建NSTimer
的线程上执行的。NSTimer
的销毁和创建必须在同一个线程上操作 -
NSTimer
要被添加到当前线程的 Runloop 里面且 Runloop 被启动,定时任务(selector
或者invocation
)才会触发。
如何创建NSTimer
对象
多数情况下,如此一行代码创建的NSTimer
就能正常工作:
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFire) userInfo:nil repeats:YES]
因为这段创建代码是在主线程里面执行的,主线程里面会有系统创建好了的且已经启动了的 Runloop :[NSRunLoop mainRunLoop]
。通过[NSTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:]
创建时,会自动将创建的NSTimer
对象加到当前的 Runloop 里面,所以 timer 能够创建后立马就能工作。
根据以上,可以这么创建自定义线程和运行在上面的 timer :
创建线程对象和NSTimer
对象,定义函数
- 子类化
NSThread
为Mythread
,重载了dealloc
和exit
函数,在里面加了 log 输出,方便跟踪执行过程-
Mythread.h
文件为默认,略去 -
Mythread.m
文件:
-
#import "Mythread.h"
@implementation Mythread
- (void)dealloc
{
NSLog(@"Thread:%p dealloc",self);
}
+ (void)exit
{
NSLog(@"Thread:%p exit",self);
// 注意这是个类函数
[super exit];
}
@end
- 创建
NSThread
和NSTimer
对象
@property (nonatomic , strong) NSThread *timerThread;
@property (nonatomic , strong) NSTimer *timer;
- 定义设置
NSTimer
的函数
- (void)createTimer
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFire) userInfo:nil repeats:YES];
// 创建的线程中,runloop是不会自己启动的,需要手动启动
[[NSRunLoop currentRunLoop] run];
NSLog(@"createTimer,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
}
- 定义
self.timer
的定时任务函数
- (void)timerFire
{
static NSInteger counter = 0;
NSLog(@"%@,main:%@,counter:%@",[NSThread currentThread],@([NSThread isMainThread]),@(counter++));
}
- 定义销毁
self.timer
的函数
- (void)destoryTimerAndThread
{
NSLog(@"destoryTimerAndThread,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
[self.timer invalidate];
@autoreleasepool {
self.timerThread = nil;
self.timer = nil;
}
// 释放打开的资源和清空申请的内存后,才可以退出,不然就会内存泄露
[Mythread exit];
}
- 定义启动新线程的函数
- (void)createAndStartThread
{
NSLog(@"createAndStartThread,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
self.timerThread = [[NSThread alloc] initWithTarget:self selector:@selector(createTimer) object:nil];
[self.timerThread start];
}
- 定义销毁新线程的函数
- (void)destoryThread
{
[self performSelector:@selector(destoryTimer) onThread:self.timerThread withObject:nil waitUntilDone:NO];
}
调用过程
- 主线程中调用
createAndStartThread
启动线程和NSTimer
- 隔一小会,主线程中调用
destoryThread
销毁NSTimer
和线程 - 隔一小会,主线程中调用
createAndStartThread
启动 - 隔一小会,主线程中调用
destoryThread
销毁NSTimer
和线程
console输出结果
16:16:07.166 : createAndStartThread,currentThread:<NSThread: 0x127d0b750>{number = 1, name = main},isMainThread:1
16:16:08.171 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:0
16:16:09.173 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:1
16:16:10.174 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:2
16:16:11.174 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:3
16:16:11.479 : destoryTimerAndThread,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0
16:16:11.481 : Thread:0x100011158 exit
16:16:11.482 : Thread:0x127d05810 dealloc
16:16:16.113 : createAndStartThread,currentThread:<NSThread: 0x127d0b750>{number = 1, name = main},isMainThread:1
16:16:17.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:4
16:16:18.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:5
16:16:19.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:6
16:16:20.126 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:7
16:16:21.126 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:8
16:16:21.382 : destoryTimerAndThread,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0
16:16:21.383 : Thread:0x100011158 exit
16:16:21.385 : Thread:0x127d21700 dealloc
示例说明
- 为了节省显示空间,删除了部分 log 头信息
- 创建和销毁时不一定要在主线程里面调用,只是为了方便比对输出结果
- 在销毁 Timer 时,也不一定就要销毁线程,这里只是演示非主线程的创建和销毁