iOS RunLoop 初识
今天突然才之間才意識(shí)到NSTimer這樣的運(yùn)行方式,是在多線程中實(shí)現(xiàn)的循環(huán)還是在主線程中去實(shí)現(xiàn)的呢。當(dāng)然不可能是在主線程中的while那么簡(jiǎn)單,那樣什么都干不了,簡(jiǎn)單看了下NSTimer是以同步方式運(yùn)行的。時(shí)間到了,消息發(fā)出后,ontimer的函數(shù)是在主線程上調(diào)用的。
我們會(huì)經(jīng)常看到這樣的代碼:
這段代碼很神奇的,因?yàn)樗麜?huì)“暫停”代碼運(yùn)行,而且程序運(yùn)行不會(huì)因?yàn)檫@里有一個(gè)while循環(huán)而受到影響。在[progress setHidden:NO]執(zhí)行之后,整個(gè)函數(shù)想暫停了一樣停在循環(huán)里面,等loadPageInBackground里面的操作都完成了以后才讓[progress setHidden:YES]運(yùn)行。這樣做就顯得簡(jiǎn)介,而且邏輯很清晰。如果你不這樣做,你就需要在loadPageInBackground里面表示load完成的地方調(diào)用[progress setHidden:YES],顯得代碼不緊湊而且容易出錯(cuò)。
?
? ? ? ? Run loop就像它的名字一樣,是你thread中的一個(gè)循環(huán)并對(duì)收到的事件進(jìn)行處理。你的代碼提供控制語(yǔ)句用來(lái)對(duì)run loop進(jìn)行執(zhí)行——換句話說(shuō):你的代碼提供while或for循環(huán)來(lái)驅(qū)動(dòng)run loop。在你的循環(huán)中,你使用run loop對(duì)象來(lái)“運(yùn)行”事件處理代碼。事件處理代碼主要進(jìn)行接收事件并調(diào)用事件處理函數(shù)。
??????? Run loop從兩個(gè)不同的事件源中接收消息。Input sources(CFRunLoopSource)投遞異步消息,通常來(lái)自于另一個(gè)thread或另一個(gè)應(yīng)用程序。Timer sources(CFRunLoopTimer)當(dāng)在計(jì)劃的時(shí)間或重復(fù)的時(shí)間間隔內(nèi)投遞同步消息。兩種事件源都使用應(yīng)用程序指定的處理方式對(duì)到達(dá)的事件進(jìn)行處理。下圖展示了run loop和不同的事件源結(jié)構(gòu)。
如果我們要寫(xiě)多線程的程序,可能就需要自己來(lái)管理Run Loop。
下面說(shuō)一下樓主提出的方法中的參數(shù):
RunMode: NSDefaultRunLoopMode,可以把這個(gè)理解為一個(gè)”過(guò)濾器“,我們可以只對(duì)自己關(guān)心的事件進(jìn)行監(jiān)視。一般NSDefaultRunLoopMode是最常用的。
啟動(dòng)run loop的方法就是lz寫(xiě)的這個(gè),它的說(shuō)明如下:
Runs the loop once, blocking for input in the specified mode until a given date.
啟動(dòng)run loop一次,在特定的run loop mode下等待輸入。
如果沒(méi)有附加input source或是timer,或是過(guò)limitDate,run loop就會(huì)退出,并且方法返回NO。
下來(lái)是Run Loop的使用場(chǎng)合:
1. 使用port或是自定義的input source來(lái)和其他線程進(jìn)行通信
2. 在線程(非主線程)中使用timer
3. 使用 performSelector…系列(如performSelectorOnThread, …)
4. 使用線程執(zhí)行周期性工作
run loop不需要?jiǎng)?chuàng)建,在線程中只需要調(diào)用[NSRunLoop currentRunLoop]就可以得到
假設(shè)我們想要等待某個(gè)異步方法的回調(diào)。比如connection。如果我們的線程中沒(méi)有啟動(dòng)run loop,是不會(huì)有效果的(因?yàn)榫€程已經(jīng)運(yùn)行完畢,正常退出了)。
?
?
何時(shí)使用?Run Loop
僅當(dāng)在為你的程序創(chuàng)建輔助線程的時(shí)候,你才需要顯式運(yùn)行一個(gè) run loop。Run loop 是程序主線程基礎(chǔ)設(shè)施的關(guān)鍵部分。所以,Cocoa 和 Carbon 程序提供了代碼運(yùn) 行主程序的循環(huán)并自動(dòng)啟動(dòng) run loop。IOS 程序中 UIApplication 的 run 方法(或 Mac OS X 中的 NSApplication)作為程序啟動(dòng)步驟的一部分,它在程序正常啟動(dòng)的時(shí) 候就會(huì)啟動(dòng)程序的主循環(huán)。類似的,RunApplicationEventLoop 函數(shù)為 Carbon 程序 啟動(dòng)主循環(huán)。如果你使用 xcode 提供的模板創(chuàng)建你的程序,那你永遠(yuǎn)不需要自己去顯 式的調(diào)用這些例程。
對(duì)于輔助線程,你需要判斷一個(gè) run loop 是否是必須的。如果是必須的,那么 你要自己配置并啟動(dòng)它。你不需要在任何情況下都去啟動(dòng)一個(gè)線程的 run loop。比 如,你使用線程來(lái)處理一個(gè)預(yù)先定義的長(zhǎng)時(shí)間運(yùn)行的任務(wù)時(shí),你應(yīng)該避免啟動(dòng) run loop。Run loop 在你要和線程有更多的交互時(shí)才需要,比如以下情況:
??使用端口或自定義輸入源來(lái)和其他線程通信???使用線程的定時(shí)器???Cocoa 中使用任何 performSelector...的方法???使線程周期性工作
?
如果你決定在程序中使用 run loop,那么它的配置和啟動(dòng)都很簡(jiǎn)單。和所有線程 編程一樣,你需要計(jì)劃好在輔助線程退出線程的情形。讓線程自然退出往往比強(qiáng)制關(guān) 閉它更好。關(guān)于更多介紹如何配置和退出一個(gè) run loop,參閱”使用 Run Loop 對(duì)象” 的介紹。
? ?上代碼:
?
使用runloop阻塞線程的正確寫(xiě)法
Runloop可以阻塞線程,等待其他線程執(zhí)行后再執(zhí)行。
比如:
@implementation ViewController{
? ? BOOL end;
}
…
– (void)viewDidLoad
{
? ? [super viewDidLoad];?
? ? NSLog(@”start new thread …”);
? ? [NSThread detachNewThreadSelector:@selector(runOnNewThread) toTarget:self withObject:nil];? ??
? ? while (!end) {
? ? ? ? NSLog(@”runloop…”);
? ? ? ? [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
? ? ? ? NSLog(@”runloop end.”);
? ? }
? ? NSLog(@”ok.”);
}
-(void)runOnNewThread{
? ? ?NSLog(@”run for new thread …”);
? ? sleep(1);
? ? end=YES;
? ? NSLog(@”end.”);
}
但是這樣做,運(yùn)行時(shí)會(huì)發(fā)現(xiàn),while循環(huán)后執(zhí)行的語(yǔ)句會(huì)在很長(zhǎng)時(shí)間后才被執(zhí)行。
那是不是可以這樣:
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
縮短runloop的休眠時(shí)間,看起來(lái)解決了上面出現(xiàn)的問(wèn)題。
不過(guò)這樣也又問(wèn)題,runloop對(duì)象被經(jīng)常性的喚醒,這違背了runloop的設(shè)計(jì)初衷。runloop的作用就是要減少cpu做無(wú)謂的空轉(zhuǎn),cpu可在空閑的時(shí)候休眠,以節(jié)約電量。
那么怎么做呢?正確的寫(xiě)法是:
-(void)runOnNewThread{
? ? ?NSLog(@”run for new thread …”);
? ? sleep(1);
? ??[self performSelectorOnMainThread:@selector(setEnd) withObject:nil waitUntilDone:NO];
? ? NSLog(@”end.”);
}
-(void)setEnd{
? ? end=YES;
}
見(jiàn)黑體斜體字部分,要將直接設(shè)置變量,改為向主線程發(fā)送消息,執(zhí)行方法。問(wèn)題得到解決。
這里要說(shuō)一下,造成while循環(huán)后語(yǔ)句延緩執(zhí)行的原因是,runloop未被喚醒。因?yàn)?#xff0c;改變變量的值,runloop對(duì)象根本不知道。延緩的時(shí)長(zhǎng)總是不定的,這是因?yàn)?#xff0c;有其他事件在某個(gè)時(shí)點(diǎn)喚醒了主線程,這才結(jié)束了while循環(huán)。那么,向主線程發(fā)送消息,將喚醒runloop,因此問(wèn)題就解決了。
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/DamonTang/archive/2012/12/07/2807088.html
總結(jié)
以上是生活随笔為你收集整理的iOS RunLoop 初识的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: [嵌入式]Bootloader的作用
- 下一篇: 会声会影X4 初学者速成 转