為什么要在非主線程創(chuàng)建NSTimer
- 將 timer 添加到主線程的Runloop里面本身會增加線程負荷
- 如果主線程因為某些原因阻塞卡頓了斤富,timer 定時任務(wù)觸發(fā)的時間精度肯定也會受到影響
- 有些定時任務(wù)不是UI相關(guān)的锻狗,本來就沒必要在主線程執(zhí)行,給主線程增加不必要的負擔(dān)轻纪。當(dāng)然也可以在定時任務(wù)執(zhí)行時,手動將任務(wù)指派到非主線程上刻帚,但這也是有額外開銷的。
NSTimer
的重要特性
-
NSTimer
上的定時任務(wù)是在創(chuàng)建NSTimer
的線程上執(zhí)行的崇众。NSTimer
的銷毀和創(chuàng)建必須在同一個線程上操作 -
NSTimer
要被添加到當(dāng)前線程的 Runloop 里面且 Runloop 被啟動航厚,定時任務(wù)(selector
或者invocation
)才會觸發(fā)。
如何創(chuàng)建NSTimer
對象
多數(shù)情況下幔睬,如此一行代碼創(chuàng)建的NSTimer
就能正常工作:
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFire) userInfo:nil repeats:YES]
因為這段創(chuàng)建代碼是在主線程里面執(zhí)行的,主線程里面會有系統(tǒng)創(chuàng)建好了的且已經(jīng)啟動了的 Runloop :[NSRunLoop mainRunLoop]
麻顶。通過[NSTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:]
創(chuàng)建時,會自動將創(chuàng)建的NSTimer
對象加到當(dāng)前的 Runloop 里面澈蚌,所以 timer 能夠創(chuàng)建后立馬就能工作。
根據(jù)以上宛瞄,可以這么創(chuàng)建自定義線程和運行在上面的 timer :
創(chuàng)建線程對象和NSTimer
對象,定義函數(shù)
- 子類化
NSThread
為Mythread
份汗,重載了dealloc
和exit
函數(shù),在里面加了 log 輸出杯活,方便跟蹤執(zhí)行過程-
Mythread.h
文件為默認,略去 -
Mythread.m
文件:
-
#import "Mythread.h"
@implementation Mythread
- (void)dealloc
{
NSLog(@"Thread:%p dealloc",self);
}
+ (void)exit
{
NSLog(@"Thread:%p exit",self);
// 注意這是個類函數(shù)
[super exit];
}
@end
- 創(chuàng)建
NSThread
和NSTimer
對象
@property (nonatomic , strong) NSThread *timerThread;
@property (nonatomic , strong) NSTimer *timer;
- 定義設(shè)置
NSTimer
的函數(shù)
- (void)createTimer
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFire) userInfo:nil repeats:YES];
// 創(chuàng)建的線程中旁钧,runloop是不會自己啟動的,需要手動啟動
[[NSRunLoop currentRunLoop] run];
NSLog(@"createTimer,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
}
- 定義
self.timer
的定時任務(wù)函數(shù)
- (void)timerFire
{
static NSInteger counter = 0;
NSLog(@"%@,main:%@,counter:%@",[NSThread currentThread],@([NSThread isMainThread]),@(counter++));
}
- 定義銷毀
self.timer
的函數(shù)
- (void)destoryTimerAndThread
{
NSLog(@"destoryTimerAndThread,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
[self.timer invalidate];
@autoreleasepool {
self.timerThread = nil;
self.timer = nil;
}
// 釋放打開的資源和清空申請的內(nèi)存后歪今,才可以退出颜矿,不然就會內(nèi)存泄露
[Mythread exit];
}
- 定義啟動新線程的函數(shù)
- (void)createAndStartThread
{
NSLog(@"createAndStartThread,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
self.timerThread = [[NSThread alloc] initWithTarget:self selector:@selector(createTimer) object:nil];
[self.timerThread start];
}
- 定義銷毀新線程的函數(shù)
- (void)destoryThread
{
[self performSelector:@selector(destoryTimer) onThread:self.timerThread withObject:nil waitUntilDone:NO];
}
調(diào)用過程
- 主線程中調(diào)用
createAndStartThread
啟動線程和NSTimer
- 隔一小會,主線程中調(diào)用
destoryThread
銷毀NSTimer
和線程 - 隔一小會骑疆,主線程中調(diào)用
createAndStartThread
啟動 - 隔一小會,主線程中調(diào)用
destoryThread
銷毀NSTimer
和線程
console輸出結(jié)果
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
示例說明
- 為了節(jié)省顯示空間箍铭,刪除了部分 log 頭信息
- 創(chuàng)建和銷毀時不一定要在主線程里面調(diào)用,只是為了方便比對輸出結(jié)果
- 在銷毀 Timer 時诈火,也不一定就要銷毀線程,這里只是演示非主線程的創(chuàng)建和銷毀