日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

RunLoop的学习

發布時間:2025/3/20 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 RunLoop的学习 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

什么是Run Loops

RunLoops是與線程相關聯的基礎部分,一個Run Loop就是事件處理循環,他是用來調度和協調接收到的事件處理。使用RunLoop的目的,就是使的線程有工作需要做的時候忙碌起來,當沒事做的時候,又可以使得線程休眠。

RunLoop管理不是自動的。我們必須手動設計線程代碼,在合適的時候啟動RunLoop,并回應到相應的事件。Cocoa和Core Foundation都提供了run loop對象來幫助我們配置和管理線程的run loop。我們的應用沒有必要顯式地創建這些對象;每個線程,包括應用程序的主線程,都有一個與之關聯的run loop。只有子線程才需要顯式地運行其run loop。App會將自動配置和來運行主線程的run loop的任務作為應用程序啟動處理的一部分。

一個RunLoop的結構

Run Loop就像它的名字一樣,它使得線程進入事件循環,能對到來的事件啟動事件處理。你的代碼中提供了流程控制說一句來實現run loop實實在在的循環部分,換句話說,你的代碼提供了while或者for循環來驅動run loop。在你的循環中,你使用run loop對象在事件到達時,運行事件處理的代碼并調起已安裝的處理程序。

Run Loop接收來自兩種不同類型的源(sources)的事件:

輸入源:異步傳遞事件,通常是來自不同的線程或不同的應用的消息。輸入源異步傳遞事件到對應的處理程序和在線程關聯的NSRunLoop對象調起runUntilDate:方法來退出事件處理。

Timer源:同步地傳遞事件,發生在每個定時器調用或周期性地調用。Timer源傳遞事件到他們的處理程序,但是不會調用run loop來退出處理。

這兩種源在事件到達時都使用應用程序特定的處理程序來處理事件。

如下圖所示,展示了run loop和不同的源的概述結構。


除了處理輸入源之外,run loops還發出關于run loop行為的通知。我們可以注冊成為run loop的觀察者,就可以接收這些通知和使用它在線程上做一些額外處理。我們可以使用Core Foundation在對應的線程上注冊成為run loop的觀察者。

Run Loop Modes

Run Loop模式是一個監視輸入源和定時器的集合和注冊成為run loop的觀察者的集合。每次要運行run loop,都需要顯示或隱式地指定某種運行的mode。只有與這種指定的mode關聯的源才會被監視和允許傳遞他們的事件,同樣地,只有與這種模式關聯的觀察者都會收到run loop行為變化的通知。與其它模式想關聯的源,直到隨后在合適的模式通過循環后,都會接收到新的事件(比如,將timer加入run loop default模式下,當滾動時,timer不會收到回調,直到停止滾動回到default模式下)。

在我們的代碼中,我們通過名稱來唯一標識mode。在Cocoa和Core Foundation中都定義了default模式和幾個常用的模式,都是通過字符串名稱來指定。我們也可以自定義模式,但是我們需要手動添加至少一個input source/timers/observers。

我們可以通過使用mode來過濾掉我們不希望接收到來自不想要的通過run loop的源。大部分情況下,我們都是使用系統定義的default模式。對于子線程,我們可以使用自定義模式在關鍵性操作時阻止低優先級的源傳遞事件。

注意:Modes是通過事件源來區分,而不是事件類型來區分。比如說,我們不能使用mode來匹配只有mouse-down事件或者只有鍵盤事件。我們可以使用modes來監聽不同系統的端口,臨時掛起定時器,甚至改變正在被監視的sources和run loop觀察者。


Input Sources

輸入源異步傳遞事件到你的線程。事件的源由輸入源的類型來決定,也就是兩種源中的其中一種:

Port-based:基于端口號的輸入源監聽應用程序的Mach端口。

Custom Input Sources:自定義輸入源監聽自定義的事件源。

系統通常實現了這兩種輸入源。唯一的不同點是它們是如何被發出信號的。port-based源是由內核(kernel)自動發出信號,而custom sources必須手動從其它線程發出信號。

當我們創建輸入源時,可以指定mode。Modes會影響任何時刻被監視的輸入源。大部分情況下,我們都讓run loop在default mode下運行,但是也可以指定自定義的mode。如果一個輸入源不是當前所監視的model,它所產生的任何事件都會被保留直接進入正常的mode。

Port-Based Sources

Cocoa和Core Foundation提供了內建支持,可以使用與port相關的對象和函數來創建基于端口的輸入源。舉個例子,在Cocoa中永遠不需要手動創建輸入源。我們只需要簡單地創建一個port對象和使用NSPort的方法。port對象為我們處理所需要的輸入源的創建和配置。

在Core Foundation中,我們必須手動創建port和source。在這兩種情況下,我們可以使用與port opaque type關聯的函數(CFMessagePortRef, or CFSocketRef) 來創建合適的對象。

Custom Input Sources

在Core Foundation中,要創建自定義輸入源,我們必須使用與CFRunLoopSourceRef關聯的函數。我們配置自定義輸入源可以使用幾個回調函數。Core Foundation會在不同點回調這些函數來配置source,處理任何到達的事件和銷毀已從run loop移除的source。

除了定義在事件到達時自定義源的行為之外,我們也必須定義事件傳遞機制。這部分源運行在單獨的線程,負責提供輸入源的數據,當數據準備好可以處理時,signaling(通知相關線程)這個消息。事件傳遞機制是我們自己來決定,但是不需要過于復雜。

Cocoa Perform Selector Source

除了基于端口的源之外,Cocoa還定義了自定義輸入源允許我們在任意線程上執行selector。就像port-based源一樣,執行selector請求會在目標線程上序列化,以減少在同一個線程中出現多個方法同步執行的問題。與port-based源不同的是,執行selector源在執行完畢后會自動將自己從run loop中移除。

當執行在其它線程執行selector時,目標線程必須要有運行的run loop。當我們創建線程時,這意味著直到啟動了run loop都會顯式地執行selector代碼。

Run Loop每次經過一個循環,就會處理隊列中所有的selector,而不僅僅是處理一個。


Timer Sources

Timer源在未來設定的時間會同步地傳遞事件到你的線程。Timers是線程通知自己去做一些事情的一種方式。比如說,搜索框可以使用定時器來初始化在一定時間就自動搜索,以便提供更多地聯想詞給用戶。

盡管它發送基于時間的通知,但定時器并不是一種實時的機制。像輸入源一樣,定時器只有與run loop的mode一樣才會發送通知。如果timer在run loop中并不是所被監視的mode,它不會觸發定時器,直到run loop的mode與timer所支持的mode一樣。

同樣地,如果run loop正在處理中,timer已經fire了,這時候會被中斷,直到下一次通過run loop才會調志處理程序。如果run loop已經不再運行了,則timer永遠不會再fire。

我們可以配置timer只產生事件一次或者重復產生。重復的timer會自動根據調度的firing time自動調度,而不是真實的firing time。比如說,如果一個timer在特定的時間調度,然后每5秒重復一次。如果firing time被延遲導致缺少一或多次調用,那么timer在缺失的周期中只會調用一次。

Run Loop Observers

與sources在適當時機異步或同步發出事件不同,observers在run loop本身執行期間,會在特定的地方發出。你可能需要到run loop observers去準備線程處理特定的事件或者在進入睡眠之前。我們可以通過以下事件來關聯run loop observers:

進入run loop

run loop將要處理timer

run loop將要處理輸入源

run loop將要進入睡眠

run loop被喚醒,但是還沒有處理事件

退出run loop

我們可以通過Core Foundation來添加run loop observers。要創建run loop observer,可以通過CFRunLoopObserverRef來創建新的實例。這個類型會跟蹤你所定義的回調函數和所感興趣的活動。

與timers類型,run-loop observers可以使用一次或者重復多次。一次性的observer會在fire之后自動從run loop移除,而重復性的observer會繼續持有。

The Run Loop Sequence Of Events

本小節講的是RunLoop事件順序。每次運行它,你的線程的run loop處理待處理的事件和給所有attached observers發出通知。處理的順序如下:

通知observers run loop已經進入

通知observers timers準備要fire

通知observers有不是基于port-based的輸入源即將要fire

fire任何已經準備好的non-port-based輸入源

如果port-based輸入源準備好且等待fire,則立即處理這個事件。然后進入步驟9

通知observers線程即將進入睡眠

讓線程進入睡眠,直到以下任何一種事件到達:

port-based輸入源有事件到達

timer fire

run loop超時

run loop被顯式喚醒

通知observers線程被喚醒

處理待處理的事件:

如果用戶定義的timer fired了,處理timer事件并重新啟動循環。進入步驟2

如果輸入源fired了,則傳遞事件

如果run loop被顯式喚醒,但是又未超時,則重啟循環,進入步驟2

通知observers run loop退出

由于observer對timer和輸入源的通知會在事件真正發生之前被傳遞,這樣就產生了間隙。如果這個間隙是很關鍵的,那么我們可以通過使用sleep和awake-from-sleep通知來幫助我們糾正這個時間間隔問題。

什么時候應該使用run loop呢?

只有當我們需要創建子線程的時候,才會需要到顯示地運行run loop。應用程序的主線程的run loop是應用啟動的基礎任務,在啟動時就會自動啟動run loop。所以我們不需要手動啟動主線程的run loop。

對于子線程,我們需要確定線程是否需要run loop,如果需要,則配置它并啟動它。我們并不問題需要啟動run loop的。比如說,如果我們開一個子線程去執行一些長時間的和預先決定的任務,我們可能不需要啟動run loop。Run loop是用于那么需要在線程中有更多地交互的場景。比如說,我們會在下面的任何一種場景中需要開啟run loop:

使用端口源或者自定義輸入源與其它線程通信

在線程中使用定時器

使用Cocoa中的任何performSelector…方法

保持線程來執行周期性的任務

Using Run Loop Objects

Run Loop對象給添加輸入源、定時器和觀察者到run loop提供了主接口。每個線程都有一個單獨的run loop與之關聯(對于子線程,若沒有調用過任何獲取run loop的方法是不會有run loop的,只有調用過,才會創建或者直接使用)。

在Cocoa中,通過NSRunLoop來創建實例,在low-level應用中,可以使用CFRunLoopRef類型,它是指針。

Getting A Run Loop Object

通過以下兩種方式來獲取run loop對象:

在Cocoa中,使用[NSRunLoop currentRunLoop]獲取

使用CFRunLoopGetCurrent()函數獲取

配置RunLoop

在子線程運行run loop之前,你必須至少添加一種輸入源或者定時器。如果run loop沒有任何的源需要監視,它就會立刻退出。

除了添加sources之外,你還可以添加觀察者來檢測runloop不同的執行狀態。要添加觀察者,可以使用CFRunLoopObserverRef指針類型和使用CFRunLoopAddObserver函數來添加到run loop中。我們只能通過Core Foundation來創建run loop觀察者,即使是Cocoa應用。

下面這段代碼展示主線程如何添加觀察者到run loop以及如何創建run loop觀察者:

Starting the Run Loop

只有子線程才有可能需要啟動run loop。Run loop必須至少有一種輸入源或者timer源來監視。如果沒有任何源,則run loop會退出。

下面的幾種方式可以啟動run loop:

無條件地:無條件進入run loop是最簡單的方式,但也是最不希望這么做的,因為這樣會導致run loop會進入永久地循環。可以添加、刪除輸入源和timer源,但是只能通過kill掉run loop才能停止。而且還不能使用自定義mode。

限時:與無條件運行run loop不同,最好是給run loop添加一個超時時間。

在特定的mode:除了添加超時時間,還可以指定mode。

Exiting the Run Loop

有兩種方法使run loop在處理事件之前,退出run loop:

給run loop設定超時時間

告訴run loop要stop

設定超時時間是比較推薦的。我們可以通過CFRunLoopStop函數來停止run loop。

Thread Safety and Run Loop Objects

Core Foundation中的Run Loop API是線程安全的(以CF開頭的API),而Cocoa中的NSRunLoop不是線程安全的。

Configuring Run Loop Sources

下面是展示如何配置不同類型的輸入源。

Defining a Custom Input Source

創建自定義輸入源涉及到以下部分:

想要處理的輸入源的信息

讓感興趣的客戶端知道如何聯系輸入源的調度程序

執行任何客戶端發送的請求處理程序


使輸入源失效的取消程序


二、舉例說明Runloop的優點。

一般情況下,當我們使用NSRunLoop的時候,代碼如下所示:

do {

[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopModebeforeDate:[NSDate distantFuture]];

} while (!done);

在上面的代碼中,參數done為NO的時候,當前runloop會一直接收處理其他輸入源,處理輸入源之后會再回到runloop中等待其他的輸入源;除非done為NO,否則當前流程一直再runloop中。

如下面的代碼片段所示,有三個按鈕,分別對應如下三個action消息,buttonNormalThreadTestPressed,buttonRunloopPressed,buttonTestPressed。

buttonNormalThreadTestPressed:啟動一個線程,在while循環中等待線程執行完再接著往下運行。

buttonRunloopPressed:啟動一個線程,使用runloop,等待線程執行完再接著往下運行。

buttonTestPressed:僅僅打印兩條日志,用來測試UI是否能立即響應的。

在本測試中,待程序運行后,做如下操作對比:

1、點擊buttonNormalThreadTestPressed,然后立刻點擊buttonTestPressed,查看日志輸出。

2、待1完成后,點擊buttonRunloopPressed,然后立刻點擊buttonTestPressed,查看日志輸出,跟1的日志做對比,即可以發現步驟2即使線程沒有完成,在runloop等待過程中,界面仍然能夠響應。

BOOLthreadProcess1Finished =NO;

-(void)threadProce1{

NSLog(@"Enter threadProce1.");

for(inti=0; i<5;i++) {

NSLog(@"InthreadProce1 count = %d.", i);

sleep(1);

}

threadProcess1Finished=YES;

NSLog(@"Exit threadProce1.");

}

BOOLthreadProcess2Finished =NO;

-(void)threadProce2{

NSLog(@"Enter threadProce2.");

for(inti=0; i<5;i++) {

NSLog(@"InthreadProce2 count = %d.", i);

sleep(1);

}

threadProcess2Finished=YES;

NSLog(@"Exit threadProce2.");

}

- (IBAction)buttonNormalThreadTestPressed:(UIButton*)sender {

NSLog(@"EnterbuttonNormalThreadTestPressed");

threadProcess1Finished=NO;

NSLog(@"Start a new thread.");

[NSThreaddetachNewThreadSelector:@selector(threadProce1)

toTarget:self

withObject:nil];

// 通常等待線程處理完后再繼續操作的代碼如下面的形式。

// 在等待線程threadProce1結束之前,調用buttonTestPressed,界面沒有響應,直到threadProce1運行完,才打印buttonTestPressed里面的日志。

while(!threadProcess1Finished) {

[NSThreadsleepForTimeInterval: 0.5];

}

NSLog(@"ExitbuttonNormalThreadTestPressed");

}

- (IBAction)buttonRunloopPressed:(id)sender {

NSLog(@"Enter buttonRunloopPressed");

threadProcess2Finished=NO;

NSLog(@"Start a new thread.");

[NSThreaddetachNewThreadSelector:@selector(threadProce2)

toTarget:self

withObject:nil];

// 使用runloop,情況就不一樣了。

// 在等待線程threadProce2結束之前,調用buttonTestPressed,界面立馬響應,并打印buttonTestPressed里面的日志。

// 這就是runloop的神奇所在

while(!threadProcess2Finished) {

NSLog(@"Begin runloop");

[[NSRunLoopcurrentRunLoop]runMode:NSDefaultRunLoopMode

beforeDate: [NSDatedistantFuture]];

NSLog(@"End runloop.");

}

NSLog(@"Exit buttonRunloopPressed");

}

- (IBAction)buttonTestPressed:(id)sender{

NSLog(@"Enter buttonTestPressed");

NSLog(@"Exit buttonTestPressed");

}

日志信息如下:

2013-04-07 14:25:22.829 Runloop[657:11303] EnterbuttonNormalThreadTestPressed

2013-04-07 14:25:22.830 Runloop[657:11303] Start a new thread.

2013-04-07 14:25:22.831 Runloop[657:1250f] Enter threadProce1.

2013-04-07 14:25:22.832 Runloop[657:1250f] In threadProce1 count = 0.

2013-04-07 14:25:23.833 Runloop[657:1250f] In threadProce1 count = 1.

2013-04-07 14:25:24.834 Runloop[657:1250f] In threadProce1 count = 2.

2013-04-07 14:25:25.835 Runloop[657:1250f] In threadProce1 count = 3.

2013-04-07 14:25:26.837 Runloop[657:1250f] In threadProce1 count = 4.

2013-04-07 14:25:27.839 Runloop[657:1250f] Exit threadProce1.

2013-04-07 14:25:27.840 Runloop[657:11303]ExitbuttonNormalThreadTestPressed

2013-04-07 14:25:27.841 Runloop[657:11303]EnterbuttonTestPressed

2013-04-07 14:25:27.842 Runloop[657:11303] Exit buttonTestPressed

2013-04-07 14:25:27.843 Runloop[657:11303] Enter buttonTestPressed

2013-04-07 14:25:27.844 Runloop[657:11303] Exit buttonTestPressed

2013-04-07 14:43:41.790 Runloop[657:11303] Enter buttonRunloopPressed

2013-04-07 14:43:41.790 Runloop[657:11303] Start a new thread.

2013-04-07 14:43:41.791 Runloop[657:11303] Begin runloop

2013-04-07 14:43:41.791 Runloop[657:14f0b] Enter threadProce2.

2013-04-07 14:43:41.792 Runloop[657:14f0b] In threadProce2 count = 0.

2013-04-07 14:43:42.542 Runloop[657:11303] End runloop.

2013-04-07 14:43:42.543 Runloop[657:11303] Begin runloop

2013-04-07 14:43:42.694 Runloop[657:11303]Enter buttonTestPressed

2013-04-07 14:43:42.694 Runloop[657:11303]Exit buttonTestPressed

2013-04-07 14:43:42.695 Runloop[657:11303] End runloop.

2013-04-07 14:43:42.696 Runloop[657:11303] Begin runloop

2013-04-07 14:43:42.793 Runloop[657:14f0b] In threadProce2 count = 1.

2013-04-07 14:43:43.326 Runloop[657:11303] End runloop.

2013-04-07 14:43:43.327 Runloop[657:11303] Begin runloop

2013-04-07 14:43:43.438 Runloop[657:11303]Enter buttonTestPressed

2013-04-07 14:43:43.438 Runloop[657:11303]Exit buttonTestPressed

2013-04-07 14:43:43.439 Runloop[657:11303] End runloop.

2013-04-07 14:43:43.440 Runloop[657:11303] Begin runloop

2013-04-07 14:43:43.795 Runloop[657:14f0b] In threadProce2 count = 2.

2013-04-07 14:43:44.797 Runloop[657:14f0b] In threadProce2 count = 3.

2013-04-07 14:43:45.798 Runloop[657:14f0b] In threadProce2 count = 4.

2013-04-07 14:43:46.800 Runloop[657:14f0b] Exit threadProce2.

三、Runloop簡單實例:

- (void)viewDidLoad

{

[superviewDidLoad];

// Doany additional setup after loading the view, typically from a nib.

[NSThreaddetachNewThreadSelector:@selector(newThreadProcess)

toTarget:self

withObject:nil];

}

- (void)newThreadProcess

{

@autoreleasepool{

獲得當前thread的Runloop

NSRunLoop* myRunLoop = [NSRunLoopcurrentRunLoop];

//設置Run loop observer的運行環境

CFRunLoopObserverContextcontext = {0,self,NULL,NULL,NULL};

//創建Run loop observer對象

//第一個參數用于分配observer對象的內存

//第二個參數用以設置observer所要關注的事件,詳見回調函數myRunLoopObserver中注釋

//第三個參數用于標識該observer是在第一次進入runloop時執行還是每次進入run loop處理時均執行

//第四個參數用于設置該observer的優先級

//第五個參數用于設置該observer的回調函數

//第六個參數用于設置該observer的運行環境

CFRunLoopObserverRefobserver =CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopAllActivities,YES,0, &myRunLoopObserver, &context);

if(observer)

{

//將Cocoa的NSRunLoop類型轉換成CoreFoundation的CFRunLoopRef類型

CFRunLoopRefcfRunLoop = [myRunLoopgetCFRunLoop];

//將新建的observer加入到當前thread的runloop

CFRunLoopAddObserver(cfRunLoop, observer,kCFRunLoopDefaultMode);

}

//

[NSTimerscheduledTimerWithTimeInterval:1

target:self

selector:@selector(timerProcess)

userInfo:nil

repeats:YES];

NSIntegerloopCount =2;

do{

//啟動當前thread的loop直到所指定的時間到達,在loop運行時,runloop會處理所有來自與該run loop聯系的inputsource的數據

//對于本例與當前run loop聯系的inputsource只有一個Timer類型的source。

//該Timer每隔1秒發送觸發事件給runloop,run loop檢測到該事件時會調用相應的處理方法。

//由于在run loop添加了observer且設置observer對所有的runloop行為都感興趣。

//當調用runUnitDate方法時,observer檢測到runloop啟動并進入循環,observer會調用其回調函數,第二個參數所傳遞的行為是kCFRunLoopEntry。

//observer檢測到runloop的其它行為并調用回調函數的操作與上面的描述相類似。

[myRunLooprunUntilDate:[NSDatedateWithTimeIntervalSinceNow:5.0]];

//當run loop的運行時間到達時,會退出當前的runloop。observer同樣會檢測到runloop的退出行為并調用其回調函數,第二個參數所傳遞的行為是kCFRunLoopExit。

loopCount--;

}while(loopCount);

}

}

voidmyRunLoopObserver(CFRunLoopObserverRefobserver,CFRunLoopActivityactivity,void*info)

{

switch(activity) {

//The entrance of the run loop, beforeentering the event processing loop.

//This activity occurs once for each callto CFRunLoopRun and CFRunLoopRunInMode

casekCFRunLoopEntry:

NSLog(@"run loop entry");

break;

//Inside the event processing loop beforeany timers are processed

casekCFRunLoopBeforeTimers:

NSLog(@"run loop before timers");

break;

//Inside the event processing loop beforeany sources are processed

casekCFRunLoopBeforeSources:

NSLog(@"run loop before sources");

break;

//Inside the event processing loop beforethe run loop sleeps, waiting for a source or timer to fire.

//This activity does not occur ifCFRunLoopRunInMode is called with a timeout of 0 seconds.

//It also does not occur in a particulariteration of the event processing loop if a version 0 source fires

casekCFRunLoopBeforeWaiting:

NSLog(@"run loop before waiting");

break;

//Inside the event processing loop afterthe run loop wakes up, but before processing the event that woke it up.

//This activity occurs only if the run loopdid in fact go to sleep during the current loop

casekCFRunLoopAfterWaiting:

NSLog(@"run loop after waiting");

break;

//The exit of the run loop, after exitingthe event processing loop.

//This activity occurs once for each callto CFRunLoopRun and CFRunLoopRunInMode

casekCFRunLoopExit:

NSLog(@"run loop exit");

break;

/*

A combination of all the precedingstages

case kCFRunLoopAllActivities:

break;

*/

default:

break;

}

}

- (void)timerProcess{

for(inti=0; i<5; i++) {

NSLog(@"In timerProcess count = %d.", i);

sleep(1);

}

}

調試打印信息如下:

2012-12-18 09:51:14.174 Texta[645:14807] run loop entry

2012-12-18 09:51:14.175 Texta[645:14807] run loop before timers

2012-12-18 09:51:14.176 Texta[645:14807] run loop before sources

2012-12-18 09:51:14.177 Texta[645:14807] run loop before waiting

2012-12-18 09:51:15.174 Texta[645:14807] run loop after waiting

2012-12-18 09:51:15.176 Texta[645:14807] In timerProcess count = 0.

2012-12-18 09:51:16.178 Texta[645:14807] In timerProcess count = 1.

2012-12-18 09:51:17.181 Texta[645:14807] In timerProcess count = 2.

2012-12-18 09:51:18.183 Texta[645:14807] In timerProcess count = 3.

2012-12-18 09:51:19.185 Texta[645:14807] In timerProcess count = 4.

2012-12-18 09:51:20.187 Texta[645:14807] run loop exit

2012-12-18 09:51:20.189 Texta[645:14807] run loop entry

2012-12-18 09:51:20.190 Texta[645:14807] run loop before timers

2012-12-18 09:51:20.191 Texta[645:14807] run loop before sources

2012-12-18 09:51:20.191 Texta[645:14807] run loop before waiting

2012-12-18 09:51:21.174 Texta[645:14807] run loop after waiting

2012-12-18 09:51:21.176 Texta[645:14807] In timerProcess count = 0.

2012-12-18 09:51:22.178 Texta[645:14807] In timerProcess count = 1.

2012-12-18 09:51:23.181 Texta[645:14807] In timerProcess count = 2.

2012-12-18 09:51:24.183 Texta[645:14807] In timerProcess count = 3.

2012-12-18 09:51:25.185 Texta[645:14807] In timerProcess count = 4.

2012-12-18 09:51:26.187 Texta[645:14807] run loop exit

四、Runloop可以阻塞線程,等待其他線程執行后再執行。

比如:

BOOLStopFlag =NO;

- (void)viewDidLoad

{

[superviewDidLoad];

// Doany additional setup after loading the view, typically from a nib.

StopFlag=NO;

NSLog(@"Start a new thread.");

[NSThreaddetachNewThreadSelector:@selector(newThreadProc)

toTarget:self

withObject:nil];

while(!StopFlag) {

NSLog(@"Beginrunloop");

[[NSRunLoopcurrentRunLoop]runMode:NSDefaultRunLoopMode

beforeDate: [NSDatedistantFuture]];

NSLog(@"Endrunloop.");

}

NSLog(@"OK");

}

-(void)newThreadProc{

NSLog(@"Enter newThreadProc.");

for(inti=0; i<10; i++) {

NSLog(@"InnewThreadProc count = %d.", i);

sleep(1);

}

StopFlag=YES;

NSLog(@"Exit newThreadProc.");

}

}

調試打印信息如下:

2012-12-18 08:50:34.220 Runloop[374:11303] Start a new thread.

2012-12-18 08:50:34.222 Runloop[374:11303] Begin runloop

2012-12-18 08:50:34.222 Runloop[374:14b03] Enter newThreadProc.

2012-12-18 08:50:34.223 Runloop[374:14b03] In newThreadProc count = 0.

2012-12-18 08:50:35.225 Runloop[374:14b03] In newThreadProc count = 1.

2012-12-18 08:50:36.228 Runloop[374:14b03] In newThreadProc count = 2.

2012-12-18 08:50:37.230 Runloop[374:14b03] In newThreadProc count = 3.

2012-12-18 08:50:38.233 Runloop[374:14b03] In newThreadProc count = 4.

2012-12-18 08:50:39.235 Runloop[374:14b03] In newThreadProc count = 5.

2012-12-18 08:50:40.237 Runloop[374:14b03] In newThreadProc count = 6.

2012-12-18 08:50:41.240 Runloop[374:14b03] In newThreadProc count = 7.

2012-12-18 08:50:42.242 Runloop[374:14b03] In newThreadProc count = 8.

2012-12-18 08:50:43.245 Runloop[374:14b03] In newThreadProc count = 9.

2012-12-18 08:50:44.247 Runloop[374:14b03] Exit newThreadProc.

2012-12-18 08:51:00.000 Runloop[374:11303] End runloop.

2012-12-18 08:51:00.001 Runloop[374:11303] OK

從調試打印信息可以看到,while循環后執行的語句會在很長時間后才被執行。因為,改變變量StopFlag的值,runloop對象根本不知道,runloop在這個時候未被喚醒。有其他事件在某個時點喚醒了主線程,這才結束了while循環,但延緩的時長總是不定的。。

將代碼稍微修改一下:

[[NSRunLoopcurrentRunLoop]runMode:NSDefaultRunLoopMode

beforeDate:[NSDatedateWithTimeIntervalSinceNow:1]];

縮短runloop的休眠時間,看起來解決了上面出現的問題。

但這樣會導致runloop被經常性的喚醒,違背了runloop的設計初衷。runloop的目的就死讓你的線程在有工作的時候忙于工作,而沒工作的時候處于休眠狀態。

最后,看下下面正確的寫法:

BOOLStopFlag =NO;

- (void)viewDidLoad

{

[superviewDidLoad];

// Doany additional setup after loading the view, typically from a nib.

StopFlag=NO;

NSLog(@"Start a new thread.");

[NSThreaddetachNewThreadSelector:@selector(newThreadProc)

toTarget:self

withObject:nil];

while(!StopFlag) {

NSLog(@"Beginrunloop");

[[NSRunLoopcurrentRunLoop]runMode:NSDefaultRunLoopMode

beforeDate: [NSDatedistantFuture]];

NSLog(@"Endrunloop.");

}

NSLog(@"OK");

}

-(void)newThreadProc{

NSLog(@"Enter newThreadProc.");

for(inti=0; i<10; i++) {

NSLog(@"InnewThreadProc count = %d.", i);

sleep(1);

}

[selfperformSelectorOnMainThread:@selector(setEnd)

withObject:nil

waitUntilDone:NO];

NSLog(@"Exit newThreadProc.");

}

-(void)setEnd{

StopFlag=YES;

}

調試打印信息如下:

2012-12-18 09:05:17.161 Runloop[410:11303] Start a new thread.

2012-12-18 09:05:17.163 Runloop[410:14a03] Enter newThreadProc.

2012-12-18 09:05:17.164 Runloop[410:14a03] In newThreadProc count = 0.

2012-12-18 09:05:17.165 Runloop[410:11303] Begin runloop

2012-12-18 09:05:18.166 Runloop[410:14a03] In newThreadProc count = 1.

2012-12-18 09:05:19.168 Runloop[410:14a03] In newThreadProc count = 2.

2012-12-18 09:05:20.171 Runloop[410:14a03] In newThreadProc count = 3.

2012-12-18 09:05:21.173 Runloop[410:14a03] In newThreadProc count = 4.

2012-12-18 09:05:22.175 Runloop[410:14a03] In newThreadProc count = 5.

2012-12-18 09:05:23.178 Runloop[410:14a03] In newThreadProc count = 6.

2012-12-18 09:05:24.180 Runloop[410:14a03] In newThreadProc count = 7.

2012-12-18 09:05:25.182 Runloop[410:14a03] In newThreadProc count = 8.

2012-12-18 09:05:26.185 Runloop[410:14a03] In newThreadProc count = 9.

2012-12-18 09:05:27.188 Runloop[410:14a03] Exit newThreadProc.

2012-12-18 09:05:27.188 Runloop[410:11303] End runloop.

2012-12-18 09:05:27.189 Runloop[410:11303] OK

把直接設置變量,改為向主線程發送消息,喚醒runloop,延時問題解決。

參考博客1

參考博客2參考博客3

總結

以上是生活随笔為你收集整理的RunLoop的学习的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。