在非主線程里面使用NSTimer創建和取消定時任務


為什么要在非主線程創建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對象,定義函數

  • 子類化NSThreadMythread,重載了deallocexit函數,在里面加了 log 輸出,方便跟蹤執行過程
    • Mythread.h文件為默認,略去
    • Mythread.m文件:
 1 #import "Mythread.h"
 2 @implementation Mythread
 3 - (void)dealloc
 4 {
 5     NSLog(@"Thread:%p dealloc",self);
 6 }
 7 + (void)exit
 8 {
 9     NSLog(@"Thread:%p exit",self);
10     // 注意這是個類函數
11     [super exit];
12 }
13 @end

 

  • 創建NSThreadNSTimer對象
1 @property (nonatomic , strong) NSThread *timerThread;
2 
3 @property (nonatomic , strong) NSTimer *timer;
  • 定義設置NSTimer的函數
 1 - (void)createTimer
 2 
 3 {
 4 
 5     self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFire) userInfo:nil repeats:YES];
 6 
 7     // 創建的線程中,runloop是不會自己啟動的,需要手動啟動
 8 
 9     [[NSRunLoop currentRunLoop] run];
10 
11     NSLog(@"createTimer,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
12 
13 }
  • 定義self.timer的定時任務函數
1 - (void)timerFire
2 
3 {
4 
5     static NSInteger counter = 0;
6 
7     NSLog(@"%@,main:%@,counter:%@",[NSThread currentThread],@([NSThread isMainThread]),@(counter++));
8 
9 }  
  • 定義銷毀self.timer的函數
 1 - (void)destoryTimerAndThread
 2 
 3 {
 4 
 5     NSLog(@"destoryTimerAndThread,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
 6 
 7     [self.timer invalidate];
 8 
 9     @autoreleasepool {
10 
11         self.timerThread = nil;
12 
13         self.timer = nil;
14 
15     }
16 
17     // 釋放打開的資源和清空申請的內存后,才可以退出,不然就會內存泄露
18 
19     [Mythread exit];
20 
21 }
  • 定義啟動新線程的函數
 1 - (void)createAndStartThread
 2 
 3 {
 4 
 5     NSLog(@"createAndStartThread,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
 6 
 7     self.timerThread = [[NSThread alloc] initWithTarget:self selector:@selector(createTimer) object:nil];
 8 
 9     [self.timerThread start];
10 
11 }
  • 定義銷毀新線程的函數
1 - (void)destoryThread
2 
3 {
4 
5     [self performSelector:@selector(destoryTimer) onThread:self.timerThread withObject:nil waitUntilDone:NO];
6 
7 }

調用過程

  1. 主線程中調用createAndStartThread啟動線程和NSTimer
  2. 隔一小會,主線程中調用destoryThread銷毀NSTimer和線程
  3. 隔一小會,主線程中調用createAndStartThread啟動
  4. 隔一小會,主線程中調用destoryThread銷毀NSTimer和線程

console輸出結果

 1 16:16:07.166 : createAndStartThread,currentThread:<NSThread: 0x127d0b750>{number = 1, name = main},isMainThread:1
 2 
 3 16:16:08.171 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:0
 4 
 5 16:16:09.173 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:1
 6 
 7 16:16:10.174 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:2
 8 
 9 16:16:11.174 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:3
10 
11 16:16:11.479 : destoryTimerAndThread,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0
12 
13 16:16:11.481 : Thread:0x100011158 exit
14 
15 16:16:11.482 : Thread:0x127d05810 dealloc
16 
17 16:16:16.113 : createAndStartThread,currentThread:<NSThread: 0x127d0b750>{number = 1, name = main},isMainThread:1
18 
19 16:16:17.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:4
20 
21 16:16:18.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:5
22 
23 16:16:19.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:6
24 
25 16:16:20.126 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:7
26 
27 16:16:21.126 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:8
28 
29 16:16:21.382 : destoryTimerAndThread,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0
30 
31 16:16:21.383 : Thread:0x100011158 exit
32 
33 16:16:21.385 : Thread:0x127d21700 dealloc

示例說明

    • 為了節省顯示空間,刪除了部分 log 頭信息
    • 創建和銷毀時不一定要在主線程里面調用,只是為了方便比對輸出結果
    • 在銷毀 Timer 時,也不一定就要銷毀線程,這里只是演示非主線程的創建和銷毀


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM