成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

IOS開發(fā)需要知道的知識-RunLoops

什么是Run Loops

Run Loops是與線程想關(guān)聯(lián)的基礎部分。一個Run Loop就是事件處理循環(huán),它是用來調(diào)度和協(xié)調(diào)接收到的事件處理。使用Run Loop的目的,就是使得線程有工作需要做時可以忙碌起來,而當沒有事可做時,又可以使得線程睡眠。
Run Loop管理不都是自動的。我們必須手動設計線程代碼,在合適的時候來啟動Run Loop,并回應到來的事件。Cocoa和Core Foundation都提供了run loop對象來幫助我們配置和管理線程的run loop。我們的應用沒有必要顯式地創(chuàng)建這些對象;每個線程,包括應用程序的主線程,都有一個與之關(guān)聯(lián)的run loop。只有子線程才需要顯式地運行其run loop。App會將自動配置和來運行主線程的run loop的任務作為應用程序啟動處理的一部分。
對于想要更深入地了解Run Loop Objects,閱讀NSRunLoop Class Reference、CFRunLoop Reference

成都創(chuàng)新互聯(lián)是專業(yè)的豐潤網(wǎng)站建設公司,豐潤接單;提供成都網(wǎng)站建設、成都做網(wǎng)站,網(wǎng)頁設計,網(wǎng)站設計,建網(wǎng)站,PHP網(wǎng)站建設等專業(yè)做網(wǎng)站服務;采用PHP框架,可快速的進行豐潤網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!


一個Run Loop的結(jié)構(gòu)

Run Loop就像它的名字一樣,它使得線程進入事件循環(huán),能對到來的事件啟動事件處理。你的代碼中提供了流程控制說一句來實現(xiàn)run loop實實在在的循環(huán)部分,換句話說,你的代碼提供了while或者for循環(huán)來驅(qū)動run loop。在你的循環(huán)中,你使用run loop對象在事件到達時,運行事件處理的代碼并調(diào)起已安裝的處理程序。
Run Loop接收來自兩種不同類型的源(sources)的事件
輸入源:異步傳遞事件,通常是來自不同的線程或不同的應用的消息。輸入源異步傳遞事件到對應的處理程序和在線程關(guān)聯(lián)的NSRunLoop對象調(diào)起runUntilDate:方法來退出事件處理。
Timer源:同步地傳遞事件,發(fā)生在每個定時器調(diào)用或周期性地調(diào)用。Timer源傳遞事件到他們的處理程序,但是不會調(diào)用run loop來退出處理。

這兩種源在事件到達時都使用應用程序特定的處理程序來處理事件。
如下圖所示,展示了run loop和不同的源的概述結(jié)構(gòu)。

IOS開發(fā)需要知道的知識-RunLoops

p_w_picpath


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


Run Loop Modes

Run Loop模式是一個監(jiān)視輸入源和定時器的集合和注冊成為run loop的觀察者的集合。每次要運行run loop,都需要顯示或隱式地指定某種運行的mode。只有與這種指定的mode關(guān)聯(lián)的源才會被監(jiān)視和允許傳遞他們的事件,同樣地,只有與這種模式關(guān)聯(lián)的觀察者都會收到run loop行為變化的通知。與其它模式想關(guān)聯(lián)的源,直到隨后在合適的模式通過循環(huán)后,都會接收到新的事件(比如,將timer加入run loop default模式下,當滾動時,timer不會收到回調(diào),直到停止?jié)L動回到default模式下)。
在我們的代碼中,我們通過名稱來唯一標識mode。在Cocoa和Core Foundation中都定義了default模式和幾個常用的模式,都是通過字符串名稱來指定。我們也可以自定義模式,但是我們需要手動添加至少一個input source/timers/observers。
我們可以通過使用mode來過濾掉我們不希望接收到來自不想要的通過run loop的源。大部分情況下,我們都是使用系統(tǒng)定義的default模式。對于子線程,我們可以使用自定義模式在關(guān)鍵性操作時阻止低優(yōu)先級的源傳遞事件。
注意:Modes是通過事件源來區(qū)分,而不是事件類型來區(qū)分。比如說,我們不能使用mode來匹配只有mouse-down事件或者只有鍵盤事件。我們可以使用modes來監(jiān)聽不同系統(tǒng)的端口,臨時掛起定時器,甚至改變正在被監(jiān)視的sources和run loop觀察者。

IOS開發(fā)需要知道的知識-RunLoops

表格.png


Input Sources

輸入源異步傳遞事件到你的線程。事件的源由輸入源的類型來決定,也就是兩種源中的其中一種:
Port-based:基于端口號的輸入源監(jiān)聽應用程序的Mach端口。
Custom Input Sources:自定義輸入源監(jiān)聽自定義的事件源。

系統(tǒng)通常實現(xiàn)了這兩種輸入源。唯一的不同點是它們是如何被發(fā)出信號的。port-based源是由內(nèi)核(kernel)自動發(fā)出信號,而custom sources必須手動從其它線程發(fā)出信號。
當我們創(chuàng)建輸入源時,可以指定mode。Modes會影響任何時刻被監(jiān)視的輸入源。大部分情況下,我們都讓run loop在default mode下運行,但是也可以指定自定義的mode。如果一個輸入源不是當前所監(jiān)視的model,它所產(chǎn)生的任何事件都會被保留直接進入正常的mode。


Port-Based Sources

Cocoa和Core Foundation提供了內(nèi)建支持,可以使用與port相關(guān)的對象和函數(shù)來創(chuàng)建基于端口的輸入源。舉個例子,在Cocoa中永遠不需要手動創(chuàng)建輸入源。我們只需要簡單地創(chuàng)建一個port對象和使用NSPort的方法。port對象為我們處理所需要的輸入源的創(chuàng)建和配置。
在Core Foundation中,我們必須手動創(chuàng)建port和source。在這兩種情況下,我們可以使用與port opaque type關(guān)聯(lián)的函數(shù)(CFMessagePortRef, or CFSocketRef) 來創(chuàng)建合適的對象。


Custom Input Sources

在Core Foundation中,要創(chuàng)建自定義輸入源,我們必須使用與CFRunLoopSourceRef關(guān)聯(lián)的函數(shù)。我們配置自定義輸入源可以使用幾個回調(diào)函數(shù)。Core Foundation會在不同點回調(diào)這些函數(shù)來配置source,處理任何到達的事件和銷毀已從run loop移除的source。
除了定義在事件到達時自定義源的行為之外,我們也必須定義事件傳遞機制。這部分源運行在單獨的線程,負責提供輸入源的數(shù)據(jù),當數(shù)據(jù)準備好可以處理時,signaling(通知相關(guān)線程)這個消息。事件傳遞機制是我們自己來決定,但是不需要過于復雜。


Cocoa Perform Selector Source

除了基于端口的源之外,Cocoa還定義了自定義輸入源允許我們在任意線程上執(zhí)行selector。就像port-based源一樣,執(zhí)行selector請求會在目標線程上序列化,以減少在同一個線程中出現(xiàn)多個方法同步執(zhí)行的問題。與port-based源不同的是,執(zhí)行selector源在執(zhí)行完畢后會自動將自己從run loop中移除。
當執(zhí)行在其它線程執(zhí)行selector時,目標線程必須要有運行的run loop。當我們創(chuàng)建線程時,這意味著直到啟動了run loop都會顯式地執(zhí)行selector代碼。
Run Loop每次經(jīng)過一個循環(huán),就會處理隊列中所有的selector,而不僅僅是處理一個。

IOS開發(fā)需要知道的知識-RunLoops

方法和說明。


Timer Sources

Timer源在未來設定的時間會同步地傳遞事件到你的線程。Timers是線程通知自己去做一些事情的一種方式。比如說,搜索框可以使用定時器來初始化在一定時間就自動搜索,以便提供更多地聯(lián)想詞給用戶。
盡管它發(fā)送基于時間的通知,但定時器并不是一種實時的機制。像輸入源一樣,定時器只有與run loop的mode一樣才會發(fā)送通知。如果timer在run loop中并不是所被監(jiān)視的mode,它不會觸發(fā)定時器,直到run loop的mode與timer所支持的mode一樣。
同樣地,如果run loop正在處理中,timer已經(jīng)fire了,這時候會被中斷,直到下一次通過run loop才會調(diào)志處理程序。如果run loop已經(jīng)不再運行了,則timer永遠不會再fire。
我們可以配置timer只產(chǎn)生事件一次或者重復產(chǎn)生。重復的timer會自動根據(jù)調(diào)度的firing time自動調(diào)度,而不是真實的firing time。比如說,如果一個timer在特定的時間調(diào)度,然后每5秒重復一次。如果firing time被延遲導致缺少一或多次調(diào)用,那么timer在缺失的周期中只會調(diào)用一次。


Run Loop Observers

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

進入run looprun loop將要處理timer
run loop將要處理輸入源
run loop將要進入睡眠
run loop被喚醒,但是還沒有處理事件
退出run loop

我們可以通過Core Foundation來添加run loop observers。要創(chuàng)建run loop observer,可以通過CFRunLoopObserverRef來創(chuàng)建新的實例。這個類型會跟蹤你所定義的回調(diào)函數(shù)和所感興趣的活動。
與timers類型,run-loop observers可以使用一次或者重復多次。一次性的observer會在fire之后自動從run loop移除,而重復性的observer會繼續(xù)持有。


The Run Loop Sequence Of Events

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

1.通知observers run loop已經(jīng)進入2.通知observers timers準備要fire3.通知observers有不是基于port-based的輸入源即將要fire4.fire任何已經(jīng)準備好的non-port-based輸入源5.如果port-based輸入源準備好且等待fire,則立即處理這個事件。然后進入步驟96.通知observers線程即將進入睡眠7.讓線程進入睡眠,直到以下任何一種事件到達:
        * port-based輸入源有事件到達
        * timer fire
        * run loop超時
        * run loop被顯式喚醒8.通知observers線程被喚醒9.處理待處理的事件:
       * 如果用戶定義的timer fired了,處理timer事件并重新啟動循環(huán)。進入步驟2
       * 如果輸入源fired了,則傳遞事件
       * 如果run loop被顯式喚醒,但是又未超時,則重啟循環(huán),進入步驟210.通知observers run loop退出

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


When Would You Use A Run Loop?

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

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

使用端口源或者自定義輸入源與其它線程通信
在線程中使用定時器
使用Cocoa中的任何performSelector…方法
保持線程來執(zhí)行周期性的任務

Using Run Loop Objects

Run Loop對象給添加輸入源、定時器和觀察者到run loop提供了主接口。每個線程都有一個單獨的run loop與之關(guān)聯(lián)(對于子線程,若沒有調(diào)用過任何獲取run loop的方法是不會有run loop的,只有調(diào)用過,才會創(chuàng)建或者直接使用)。
在Cocoa中,通過NSRunLoop來創(chuàng)建實例,在low-level應用中,可以使用CFRunLoopRef類型,它是指針。


Getting A Run Loop Object

通過以下兩種方式來獲取run loop對象:
在Cocoa中,使用[NSRunLoop currentRunLoop]獲取
使用CFRunLoopGetCurrent()函數(shù)獲取


配置RunLoop

在子線程運行run loop之前,你必須至少添加一種輸入源或者定時器。如果run loop沒有任何的源需要監(jiān)視,它就會立刻退出。
除了添加sources之外,你還可以添加觀察者來檢測runloop不同的執(zhí)行狀態(tài)。要添加觀察者,可以使用CFRunLoopObserverRef指針類型和使用CFRunLoopAddObserver函數(shù)來添加到run loop中。我們只能通過Core Foundation來創(chuàng)建run loop觀察者,即使是Cocoa應用。
下面這段代碼展示主線程如何添加觀察者到run loop以及如何創(chuàng)建run loop觀察者:

- (void)threadMain {    // 應用程序使用垃圾收集,因此不需要autorelease池。
    NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];    // 創(chuàng)建一個運行循環(huán)觀察者并將它附加到運行循環(huán)。
    CFRunLoopObserverContext  context = {0, self, NULL, NULL, NULL};    CFRunLoopObserverRef    observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
            kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);    if (observer) {        CFRunLoopRef    cfLoop = [myRunLoop getCFRunLoop];        CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
    }    //創(chuàng)建和安排計時器。
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self
                selector:@selector(doFireTimer:) userInfo:nil repeats:YES];    NSInteger    loopCount = 10;    do {        // 運行運行循環(huán)10次讓計時器。
        [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
        loopCount--;
    } while (loopCount);
}

在為長時間存在的線程配置run-loop時,最好添加至少一個輸入源來接收事件。盡管我們可以只用timer源,但是一旦timer調(diào)用后,經(jīng)常會被invalidate,這會導致run loop退出。


Starting the Run Loop

只有子線程才有可能需要啟動run loop。Run loop必須至少有一種輸入源或者timer源來監(jiān)視。如果沒有任何源,則run loop會退出。
下面的幾種方式可以啟動run loop:
無條件地:無條件進入run loop是最簡單的方式,但也是最不希望這么做的,因為這樣會導致run loop會進入永久地循環(huán)。可以添加、刪除輸入源和timer源,但是只能通過kill掉run loop才能停止。而且還不能使用自定義mode。
限時:與無條件運行run loop不同,最好是給run loop添加一個超時時間。
在特定的mode:除了添加超時時間,還可以指定mode。

下面是運行run loop的一段代碼:

- (void)skeletonThreadMain {    //建立一個autorelease如果不使用垃圾收集池。
    BOOL done = NO;    //添加你的來源或計時器運行循環(huán),做其他任何設置。

    do {/ /啟動運行循環(huán)但每個源處理后返回。
        SInt32    result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);

      / /如果源明確停止運行循環(huán),或如果沒有源或計時器,然后退出。        if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished))
            done = YES;

/ /檢查任何其他退出條件和設置完成所需的變量。    } while (!done);

    / /清理代碼。一定要釋放任何生成自動分配池。
}

Exiting the Run Loop

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

給run loop設定超時時間
告訴run loop要stop

設定超時時間是比較推薦的。我們可以通過CFRunLoopStop函數(shù)來停止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

創(chuàng)建自定義輸入源涉及到以下部分:
想要處理的輸入源的信息
讓感興趣的客戶端知道如何聯(lián)系輸入源的調(diào)度程序
執(zhí)行任何客戶端發(fā)送的請求處理程序
使輸入源失效的取消程序


Timer Source

NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];

/ /創(chuàng)建和安排第一個定時器。NSDate* futureDate = [NSDate dateWithTimeIntervalSinceNow:1.0];NSTimer* myTimer = [[NSTimer alloc] initWithFireDate:futureDate
                        interval:0.1
                        target:self
                        selector:@selector(myDoFireTimer1:)
                        userInfo:nil[myRunLoop addTimer:myTimer forMode:NSDefaultRunLoopMode];

或者使用Core Foundation:

CFRunLoopRef runLoop = CFRunLoopGetCurrent();CFRunLoopTimerContext context = {0, NULL, NULL, NULL, NULL};CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, 0.1, 0.3, 0, 0,
                                        &myCFTimerCallback, &context);CFRunLoopAddTimer(runLoop, timer, kCFRunLoopCommonModes);

最后
本篇文章主要是官方文檔的部分翻譯版本,不過有很多無關(guān)的都省略了,而且有都轉(zhuǎn)換成筆者的語言來表達出來,如果讀不懂,最好還是去看官方文檔吧。畢竟,英文與中文翻譯不管怎么翻譯都存在很大的問題。
疑問
官方文檔中提到,每個線程都有一個run loop與之關(guān)聯(lián)。但是實質(zhì)上子線程在沒有訪問過run loop時,是不存在的。當訪問時,若不存在則創(chuàng)建run loop并放到全局數(shù)組中。


文章標題:IOS開發(fā)需要知道的知識-RunLoops
轉(zhuǎn)載源于:http://jinyejixie.com/article46/jjipeg.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃建站公司、商城網(wǎng)站、網(wǎng)站內(nèi)鏈、動態(tài)網(wǎng)站面包屑導航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運營
思茅市| 临沂市| 繁昌县| 清苑县| 肥城市| 南平市| 施甸县| 吉安县| 乌鲁木齐市| 元谋县| 平谷区| 诏安县| 赣州市| 永城市| 永城市| 武穴市| 广元市| 广元市| 荣成市| 炎陵县| 沅江市| 潜山县| 将乐县| 侯马市| 涿州市| 达尔| 府谷县| 元谋县| 华容县| 虹口区| 阿瓦提县| 山西省| 宁南县| 靖江市| 习水县| 长治县| 綦江县| 大庆市| 蕲春县| 普定县| 西宁市|