系統(tǒng)為我們提供了多種模式,下面列一些比較常遇到的:
kCFRunLoopDefaultMode: App的默認(rèn) Mode,通常主線程是在這個(gè) Mode 下運(yùn)行的。
UITrackingRunLoopMode: 界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響。
UIInitializationRunLoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用。
NSRunLoopCommonModes: 包含了多種模式:default, modal, 和tracking modes。
runloop 和線程的關(guān)系:
1 run oop和線程是緊密相連的,可以這樣說run loop是為了線程而生,沒有線程,它就沒有存在的必要。Run loops是線程的基礎(chǔ)架構(gòu)部分
2 一條線程對(duì)應(yīng)一個(gè)RunLoop對(duì)象,每條線程都有唯一一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象。
3 我們只能在當(dāng)前線程中操作當(dāng)前線程的RunLoop,而不能去操作其他線程的RunLoop。
4 RunLoop對(duì)象在第一次獲取RunLoop時(shí)創(chuàng)建,銷毀則是在線程結(jié)束的時(shí)候。
5 主線程的RunLoop對(duì)象系統(tǒng)自動(dòng)幫助我們創(chuàng)建好了(原理如下),而子線程的RunLoop對(duì)象需要我們主動(dòng)創(chuàng)建。
使用:
1、NSTimer的創(chuàng)建使用
當(dāng)實(shí)例化NSTimer對(duì)象的時(shí)候,通常會(huì)使用 scheduledTimerWithTimeInterval 方法。該方法會(huì)自動(dòng)為我們實(shí)例化的timer添加到當(dāng)前線程的RunLoop中,并且默認(rèn)模式是?NSDefaultRunLoopMode。但當(dāng)前線程是主線程時(shí),某些UI事件,比如ScrollView正在拖動(dòng),將會(huì)RunLoop切換成 NSEventTrackingRunLoopMode 模式,在這個(gè)模式下,默認(rèn)的?NSDefaultRunLoopMode 模式中注冊(cè)的事件是不會(huì)執(zhí)行的。也就是說,使用?scheduledTimerWithTimeInterval 方法添加到RunLoop中的Timer就不會(huì)執(zhí)行。
為了設(shè)置一個(gè)不被UI干擾的Timer,我們需要手動(dòng)創(chuàng)建一個(gè)Timer,然后使用RunLoop的 addTimer:forMode: 方法來把Timer按照指定的模式加入到RunLoop中。這里使用?NSRunLoopCommonModes 模式,這個(gè)模式相當(dāng)于?NSDefaultRunLoopMode 和?NSEventTrackingRunLoopMode 的結(jié)合。
2 ImageView推遲顯示
一些動(dòng)畫執(zhí)行過程中,
或者scrllview滑動(dòng)過程匯總image的顯示
[cell performSelector:@selector(setImage)withObject:nil afterDelay:0 inModes:@[NSDefaultRunLoopMode]];
[imageview?performSelector: inModes: ]
3 后臺(tái)常駐線程(很常用)
我們?cè)陂_發(fā)應(yīng)用程序的過程中,如果后臺(tái)操作特別頻繁,經(jīng)常會(huì)在子線程做一些耗時(shí)操作(下載文件、后臺(tái)播放音樂等),我們最好能讓這條線程永遠(yuǎn)常駐內(nèi)存。
那么怎么做呢?
添加一條用于常駐內(nèi)存的強(qiáng)引用的子線程,在該線程的RunLoop下添加一個(gè)Sources,開啟RunLoop。
具體實(shí)現(xiàn)過程如下:
在項(xiàng)目的ViewController.m中添加一條強(qiáng)引用的thread線程屬性,如下圖:

在viewDidLoad中創(chuàng)建線程self.thread,使線程啟動(dòng)并執(zhí)行run1方法,代碼如下。在Demo中,請(qǐng)?jiān)趘iewDidLoad調(diào)用[self showDemo4];方法。

運(yùn)行之后發(fā)現(xiàn)打印了----run1-----,而未開啟RunLoop則未打印。
這時(shí),我們就開啟了一條常駐線程,下邊我們來試著添加其他任務(wù),除了之前創(chuàng)建的時(shí)候調(diào)用了run1方法,我們另外在點(diǎn)擊的時(shí)候調(diào)用run2方法。
那么,我們?cè)趖ouchesBegan中調(diào)用PerformSelector,從而實(shí)現(xiàn)在點(diǎn)擊屏幕的時(shí)候調(diào)用run2方法。具體代碼如下:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{ ? ? ? //利用performSelector,在self.thread的線程中調(diào)用run2方法執(zhí)行任務(wù)
? ? [self performSelector:@selector(run2)onThread:self.thread withObject:nil waitUntilDone:NO];
}
-(void)run2
{
?? ?NSLog(@"----run2------");
}
經(jīng)過運(yùn)行測(cè)試,除了之前打印的----run1-----,每當(dāng)我們點(diǎn)擊屏幕,都能調(diào)用----run2------。
這樣我們就實(shí)現(xiàn)了常駐線程的需求。