iOS多執行緒的初步研究(四)-- NSTimer

憶江南的部落格發表於2015-07-28

理解run loop後,才能徹底理解NSTimer的實現原理,也就是說NSTimer實際上依賴run loop實現的。

先看看NSTimer的兩個常用方法:

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo; //生成timer但不執行

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo; //生成timer並且納入當前執行緒的run loop來執行

NSRunLoop與timer有關方法為:

- (void)addTimer:(NSTimer *)timer forMode:(NSString *)mode; //在run loop上註冊timer

主執行緒已經有run loop,所以NSTimer一般在主執行緒上執行都不必再呼叫addTimer:。但在非主執行緒上執行必須配置run loop,該執行緒的main方法示例程式碼如下:

- (void)main

{

  NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timer:) userInfo:nil repeats:YES];

  NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

  [runLoop addTimer:myTimer forMode:NSDefaultRunLoopMode]; //實際上這步是不需要,scheduledTimerWithTimeInterval已經納入當前執行緒執行。如果使用timerWithTimeInterval則需要

  while (condition)

    [runLoop run];

}

實際上這個執行緒無法退出,因為有timer事件需要處理,[runLoop run]會一直無法返回。解決辦法就是設定一個截止時間:

[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]]; //每隔10秒檢查下執行緒迴圈條件,當然時間值可以根據實際情況來定。

 

我們通常在主執行緒中使用NSTimer,有個實際遇到的問題需要注意。當滑動介面時,系統為了更好地處理UI事件和滾動顯示,主執行緒runloop會暫時停止處理一些其它事件,這時主執行緒中執行的NSTimer就會被暫停。解決辦法就是改變NSTimer執行的mode(mode可以看成事件型別),不使用預設的NSDefaultRunLoopMode,而是改用NSRunLoopCommonModes,這樣主執行緒就會繼續處理NSTimer事件了。具體程式碼如下:

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timer:) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoopaddTimer:timer forMode:NSRunLoopCommonModes];

大家可以參看博文http://bluevt.org/?p=209,加深理解NSTimer和NSRunLoop的關係。

 

以前博文中提到延遲呼叫的方法,其實就是在當前執行緒的run loop上註冊timer來實現定時執行的。所以如果是在非主執行緒上使用,一定要有一個run loop。

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;

相關文章