iOS NSTimer 從入門到實戰(zhàn)

定時器

一. 初始化

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

注:不用scheduled方式初始化的,需要手動addTimer:forMode: 將timer添加到一個runloop中。
而scheduled的初始化方法將以默認mode直接添加到當前的runloop中。

二. 觸發(fā)
當定時器創(chuàng)建完(不用scheduled的,添加到runloop中后,該定時器將在初始化時指定的timeInterval秒后自動觸發(fā)。
可以使用-(void)fire;方法來立即觸發(fā)該定時器;
注:You can use this method to fire a repeating timer without interrupting its regular firing schedule. If the timer is non-repeating, it is automatically invalidated after firing, even if its scheduled fire date has not arrived.
在重復執(zhí)行的定時器中調(diào)用此方法后立即觸發(fā)該定時器,但不會中斷其之前的執(zhí)行計劃;
在不重復執(zhí)行的定時器中調(diào)用此方法,立即觸發(fā)后,就會使這個定時器失效。

三. 停止
- (void)invalidate;
這個是唯一一個可以將計時器從runloop中移出的方法。
注:
NSTimer可以精確到50-100毫秒.
NSTimer不是絕對準確的,而且中間耗時或阻塞錯過下一個點,那么下一個點就pass過去了.

四. 使用
NSTimer的使用一般分三種情況,分別是NSRunLoopCommonModes和Timer 、NSThread和Timer以及GCD中的Timer。

NSRunLoopCommonModes和Timer

當使用NSTimer的scheduledTimerWithTimeInterval方法時。事實上此時Timer會被加入到當前線程的Run Loop中,且模式是默認的NSDefaultRunLoopMode。而如果當前線程就是主線程,也就是UI線程時,某些UI事件,比如UIScrollView的拖動操作,會將Run Loop切換成NSEventTrackingRunLoopMode模式,在這個過程中,默認的NSDefaultRunLoopMode模式中注冊的事件是不會被執(zhí)行的。也就是說,此時使用scheduledTimerWithTimeInterval添加到Run Loop中的Timer就不會執(zhí)行。
所以為了設(shè)置一個不被UI干擾的Timer,我們需要手動創(chuàng)建一個Timer,然后使用NSRunLoop的addTimer:forMode:方法來把Timer按照指定模式加入到Run Loop中。這里使用的模式是:NSRunLoopCommonModes,這個模式等效于NSDefaultRunLoopMode和NSEventTrackingRunLoopMode的結(jié)合。
參考代碼:

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"主線程 %@", [NSThread currentThread]);
    //創(chuàng)建Timer
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(timer_callback)userInfo:nil repeats:YES];
    //使用NSRunLoopCommonModes模式,把timer加入到當前Run Loop中。
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
//timer的回調(diào)方法
- (void)timer_callback
{
    NSLog(@"Timer %@", [NSThread currentThread]);
}

輸出:

主線程 <NSThread: 0x71501e0>{name = (null), num = 1}
Timer <NSThread: 0x71501e0>{name = (null), num = 1}
Timer <NSThread: 0x71501e0>{name = (null), num = 1}
Timer <NSThread: 0x71501e0>{name = (null), num = 1}

NSThread和Timer

上面講的NSRunLoopCommonModes和Timer中有一個問題,這個Timer本質(zhì)上是在當前線程的Run Loop中循環(huán)執(zhí)行的,因此Timer的回調(diào)方法不是在另一個線程的。那么怎樣在真正的多線程環(huán)境下運行一個Timer呢?
可以先試試NSThread。同上,我們還是會把Timer加到Run Loop中,只不過這個是在另一個線程中,因此我們需要手動執(zhí)行Run Loop(通過NSRunLoop的run方法),同時注意在新的線程執(zhí)行中加入@autoreleasepool(非ARC)。
參考代碼:

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"主線程 %@", [NSThread currentThread]);
    //創(chuàng)建并執(zhí)行新的線程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
    [thread start];
}
- (void)newThread
{
    //在當前Run Loop中添加timer,模式是默認的NSDefaultRunLoopMode
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timer_callback)userInfo:nil repeats:YES];
    //開始執(zhí)行新線程的Run Loop
    [[NSRunLoop currentRunLoop] run];
}
//timer的回調(diào)方法
- (void)timer_callback
{
    NSLog(@"Timer %@", [NSThread currentThread]);
}

輸出:

主線程 <NSThread: 0x7118800>{name = (null), num = 1}
Timer <NSThread: 0x715c2e0>{name = (null), num = 3}
Timer <NSThread: 0x715c2e0>{name = (null), num = 3}
Timer <NSThread: 0x715c2e0>{name = (null), num = 3}

GCD中的Timer

GCD中的Timer應該是最靈活的,而且是多線程的。GCD中的Timer是靠Dispatch Source來實現(xiàn)的。
因此先需要聲明一個dispatch_source_t本地變量:

@interface ViewController () {
    dispatch_source_t _timer;
}

參考代碼:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"主線程 %@", [NSThread currentThread]);
    // 間隔為 2 秒鐘
    uint64_t interval = 2 * NSEC_PER_SEC;
    // 創(chuàng)建一個專門執(zhí)行timer回調(diào)的GCD隊列
    dispatch_queue_t queue = dispatch_queue_create("myTimerQueue", 0);
    // 創(chuàng)建timer
    _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    // 使用 dispatch_source_set_timer 函數(shù)設(shè)置timer 參數(shù)
    dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, 0);
    // 設(shè)置回調(diào)
    dispatch_source_set_event_handler(_timer, ^{
        NSLog(@"timer: %@", [NSThread currentThread]);
    });
    // dispatch_source默認是Suspended狀態(tài),通過dispatch_resume函數(shù)開始它
    dispatch_resume(_timer);
    // dispatch_suspend(_timer); 暫停定時器
}

輸出:

主線程 <NSThread: 0x711fab0>{name = (null), num = 1}
Timer <NSThread: 0x713a380>{name = (null), num = 3}
Timer <NSThread: 0x713a380>{name = (null), num = 3}
Timer <NSThread: 0x713a380>{name = (null), num = 3}
最后編輯于
?著作權(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)容