1. NSRunLoopCommonModes和Timer
- 當(dāng)使用NSTimer的scheduledTimerWithTimeInterval方法時(shí)匕坯。事實(shí)上此時(shí)Timer會(huì)被加入到當(dāng)前線程的Run Loop中袭异,且模式是默認(rèn)的NSDefaultRunLoopMode掀亩。而如果當(dāng)前線程就是主線程菜谣,也就是UI線程時(shí)衰伯,某些UI事件砍的,比如UIScrollView的拖動(dòng)操作蹂喻,會(huì)將Run Loop切換成NSEventTrackingRunLoopMode模式筏餐,在這個(gè)過(guò)程中开泽,默認(rèn)的NSDefaultRunLoopMode模式中注冊(cè)的事件是不會(huì)被執(zhí)行的。也就是說(shuō)魁瞪,此時(shí)使用scheduledTimerWithTimeInterval添加到Run Loop中的Timer就不會(huì)執(zhí)行穆律。
- 所以為了設(shè)置一個(gè)不被UI干擾的Timer,我們需要手動(dòng)創(chuàng)建一個(gè)Timer导俘,然后使用NSRunLoop的addTimer:forMode:方法來(lái)把Timer按照指定模式加入到Run Loop中峦耘。這里使用的模式是:NSRunLoopCommonModes,這個(gè)模式等效于NSDefaultRunLoopMode和NSEventTrackingRunLoopMode的結(jié)合旅薄。(參考Apple文檔)
參考代碼:
- (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加入到當(dāng)前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}
2. NSThread和Timer
上面講的NSRunLoopCommonModes和Timer中有一個(gè)問(wèn)題少梁,這個(gè)Timer本質(zhì)上是在當(dāng)前線程的Run Loop中循環(huán)執(zhí)行的洛口,因此Timer的回調(diào)方法不是在另一個(gè)線程的。那么怎樣在真正的多線程環(huán)境下運(yùn)行一個(gè)Timer呢凯沪?
可以先試試NSThread第焰。同上,我們還是會(huì)把Timer加到Run Loop中妨马,只不過(guò)這個(gè)是在另一個(gè)線程中挺举,因此我們需要手動(dòng)執(zhí)行Run Loop(通過(guò)NSRunLoop的run方法)而叼,同時(shí)注意在新的線程執(zhí)行中加入@autoreleasepool。
完整代碼如下:
- (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
{
@autoreleasepool
{
//在當(dāng)前Run Loop中添加timer豹悬,模式是默認(rèn)的NSDefaultRunLoopMode
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timer_callback)userInfo:nil repeats:YES];
//開(kāi)始執(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}
[返回目錄](méi)(http://www.cnblogs.com/mgen/p/3276722.html#_hContent)
3. GCD中的Timer
GCD中的Timer應(yīng)該是最靈活的葵陵,而且是多線程的。GCD中的Timer是靠Dispatch Source來(lái)實(shí)現(xiàn)的瞻佛。
因此先需要聲明一個(gè)dispatch_source_t本地變量:
@interface ViewController ()
{
dispatch_source_t _timer;
}
接著通過(guò)dispatch_source_create函數(shù)來(lái)創(chuàng)建一個(gè)專(zhuān)門(mén)的Dispatch Source脱篙,接著通過(guò)dispatch_source_set_timer函數(shù)來(lái)設(shè)置Timer的參數(shù),注意這里的時(shí)間參數(shù)有些蛋疼伤柄。
開(kāi)始時(shí)間的類(lèi)型是dispatch_time_t绊困,最好用dispatch_time或者dispatch_walltime函數(shù)來(lái)創(chuàng)建dispatch_time_t對(duì)象。如果需要Timer立即執(zhí)行适刀,可以傳入dispatch_time(DISPATCH_TIME_NOW, 0)秤朗。
internal和leeway參數(shù)分別表示Timer的間隔時(shí)間和精度。類(lèi)型都是uint64_t笔喉。間隔時(shí)間的單位竟然是納秒取视。可以借助預(yù)定義的NSEC_PER_SEC宏常挚,比如如果間隔時(shí)間是兩秒的話作谭,那interval參數(shù)就是:2 * NSEC_PER_SEC。
leeway就是精度參數(shù)奄毡,代表系統(tǒng)可以延時(shí)的時(shí)間間隔折欠,最高精度當(dāng)然就傳0。
然后通過(guò)dispatch_source_set_event_handler函數(shù)來(lái)設(shè)置Dispatch Source的事件回調(diào)吼过,這里當(dāng)然是使用Block了锐秦。
最后所有dispatch_source_t創(chuàng)建后默認(rèn)都是暫停狀態(tài)的,所以必須通過(guò)dispatch_resume函數(shù)來(lái)開(kāi)始事件監(jiān)聽(tīng)盗忱。這里就代表著開(kāi)始Timer酱床。
完整代碼:
NSLog(@"主線程 %@", [NSThread currentThread]);
//間隔還是2秒
uint64_t interval = 2 * NSEC_PER_SEC;
//創(chuàng)建一個(gè)專(zhuān)門(mén)執(zhí)行timer回調(diào)的GCD隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("my queue", 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默認(rèn)是Suspended狀態(tài),通過(guò)dispatch_resume函數(shù)開(kāi)始它
dispatch_resume(_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