將NSTimer新增至RunLoop中的兩種方法區別

憶江南的部落格發表於2015-07-18
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
     
    //用NSObject的方法建立一個多執行緒
    [self performSelectorInBackground:@selector(multiThread) withObject:nil];
     
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}
- (void)multiThread
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    if (![NSThread isMainThread]) {
         
        // 第1種方式
        //此種方式建立的timer已經新增至runloop中
//        [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
        //保持執行緒為活動狀態,才能保證定時器執行
//        [[NSRunLoop currentRunLoop] run];//已經將nstimer新增到NSRunloop中了
         
        //第2種方式
        //此種方式建立的timer沒有新增至runloop中
       NSTimer *timer = [NSTimer timerWithTimeInterval:1.0f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
        //將定時器新增到runloop中
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
        NSLog(@"多執行緒結束");
    }
     [pool release];
}
 
- (void)timerAction
{
    //定時器也是在子執行緒中執行的
    if (![NSThread isMainThread]) {
        NSLog(@"定時器");
    }
}








理解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;


相關文章