在非主線程里面使用`NSTimer`創(chuàng)建和取消定時任務(wù)

為什么要在非主線程創(chuàng)建NSTimer

  • 將 timer 添加到主線程的Runloop里面本身會增加線程負荷
  • 如果主線程因為某些原因阻塞卡頓了,timer 定時任務(wù)觸發(fā)的時間精度肯定也會受到影響
  • 有些定時任務(wù)不是UI相關(guān)的,本來就沒必要在主線程執(zhí)行,給主線程增加不必要的負擔。當然也可以在定時任務(wù)執(zhí)行時,手動將任務(wù)指派到非主線程上,但這也是有額外開銷的。

NSTimer的重要特性

  • NSTimer上的定時任務(wù)是在創(chuàng)建NSTimer的線程上執(zhí)行的。NSTimer的銷毀和創(chuàng)建必須在同一個線程上操作
  • NSTimer要被添加到當前線程的 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對象加到當前的 Runloop 里面,所以 timer 能夠創(chuàng)建后立馬就能工作。

根據(jù)以上,可以這么創(chuàng)建自定義線程和運行在上面的 timer :

創(chuàng)建線程對象和NSTimer對象,定義函數(shù)

  • 子類化NSThreadMythread,重載了deallocexit函數(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)建NSThreadNSTimer對象
@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)用過程

  1. 主線程中調(diào)用createAndStartThread啟動線程和NSTimer
  2. 隔一小會,主線程中調(diào)用destoryThread銷毀NSTimer和線程
  3. 隔一小會,主線程中調(diào)用createAndStartThread啟動
  4. 隔一小會,主線程中調(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)建和銷毀
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容