欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

ios 多線(xiàn)程開(kāi)發(fā)(三)Run Loops

系統(tǒng) 2308 0

Run loops是線(xiàn)程相關(guān)的一些基本東西。一個(gè)run loop是一個(gè)處理消息的循環(huán)。用來(lái)處理計(jì)劃任務(wù)或者收到的事件。run loop的作用是在有事做的時(shí)候保持線(xiàn)程繁忙,沒(méi)事的時(shí)候讓線(xiàn)程掛起。

Run loop的管理并不是完全自動(dòng)的。你仍然需要設(shè)計(jì)代碼來(lái)在合適的時(shí)候啟動(dòng)run loop來(lái)相應(yīng)事件。Cocoa和Core Foundation都提供了run loop對(duì)象來(lái)配置和管理run loop。程序并不需要?jiǎng)?chuàng)建這些對(duì)象,每個(gè)線(xiàn)程,包括主線(xiàn)程都有一個(gè)對(duì)應(yīng)的run loop對(duì)象。只有非主線(xiàn)程需要明確的啟動(dòng)它的run loop。自動(dòng)啟動(dòng)主線(xiàn)程的run loop是app框架啟動(dòng)流程的一部分。

下面會(huì)介紹一下run loop以及如何配置它。

?

Run Loop 詳解

Run loop正如它名字所說(shuō)的一樣。是線(xiàn)程進(jìn)入的一個(gè)環(huán),用來(lái)處理接收和處理事件。你需要寫(xiě)代碼來(lái)控制run loop實(shí)際的循環(huán),也就是說(shuō),你需要提供驅(qū)動(dòng)run loop的while或者for循環(huán)。在循環(huán)中,使用run loop對(duì)象來(lái)處理事件,接收事件以及調(diào)用對(duì)應(yīng)的處理程序。

run loop接收兩種源。輸入源傳遞異步的消息,通常是其他線(xiàn)程或其他程序發(fā)送過(guò)來(lái)的。定時(shí)器源傳遞同步事件,在一個(gè)計(jì)劃的時(shí)間或重復(fù)的時(shí)間間隔產(chǎn)生。兩種類(lèi)型都使用程序指定的處理程序來(lái)處理事件。

下面的圖展示了run loop和它的消息源的概念。輸入源傳遞異步事件給對(duì)應(yīng)的處理程序并且導(dǎo)致runUntilDate:方法退出。Timer發(fā)送事件給對(duì)應(yīng)的處理程序但是不會(huì)導(dǎo)致run loop退出

? ios 多線(xiàn)程開(kāi)發(fā)(三)Run Loops

另外,run loop有時(shí)候會(huì)發(fā)出廣播。注冊(cè)run-loop observers可以接收到這些廣播然后來(lái)在線(xiàn)程上做你想做的事。可以使用Core Foundation來(lái)在線(xiàn)程上設(shè)置run-loop observers

?

Run Loop Modes

一個(gè)run loop mode是一個(gè)需要監(jiān)控和處理的輸入源和定時(shí)器的集合。每次運(yùn)行run loop,都可以設(shè)置一個(gè)類(lèi)型來(lái)運(yùn)行。在這種情況下,只有和這種類(lèi)型相關(guān)的事件才會(huì)被接收到。(也就是說(shuō),只有和這種類(lèi)型相關(guān)的事件才會(huì)通知run loop的執(zhí)行程序。)其他類(lèi)型相關(guān)的源會(huì)掛起直到有對(duì)應(yīng)的類(lèi)型來(lái)接收它。

代碼中使用名字來(lái)標(biāo)識(shí)類(lèi)型。Cocoa和Core Foundation都定義了一個(gè)默認(rèn)類(lèi)型以及其他幾個(gè)通常用到的類(lèi)型,也是通過(guò)字符串來(lái)標(biāo)識(shí)他們的。你可以給類(lèi)型名字指定一個(gè)字符串來(lái)自定義類(lèi)型。雖然你可以自定義任何名字,但是類(lèi)型的內(nèi)容并不是任意的。你必須要添加一個(gè)或多個(gè)輸入源,定時(shí)器或run-loop observer來(lái)讓他們有意義。

使用類(lèi)型來(lái)過(guò)濾run loop的事件。大多數(shù)時(shí)候,都會(huì)運(yùn)行系統(tǒng)默認(rèn)的類(lèi)型。modal panel可能會(huì)使用"modal"類(lèi)型。在這種類(lèi)型下,只有和modal pannel相關(guān)的事件會(huì)發(fā)送。對(duì)于其他線(xiàn)程,可以使用自定義的類(lèi)型來(lái)過(guò)濾低優(yōu)先級(jí)的源。

提示:類(lèi)型是根據(jù)事件源的類(lèi)型,而不是事件的類(lèi)型。比如,你不僅僅需要只需要鼠標(biāo)按下或鍵盤(pán)事件。可能還需要監(jiān)聽(tīng)端口,定時(shí)器掛起,或源的改變等。

下面是Cocoa和Core Foundation預(yù)定義的一些類(lèi)型。

Mode 名字 描述
Defaule

NSDefaultRunLoopMode(Cocoa)

kCFRunLoopDefaultMode(Core Foundation)

默認(rèn)類(lèi)型是大部分操作用到的。大多數(shù)時(shí)候都使用這種類(lèi)型來(lái)啟動(dòng)run loop以及配置輸入源。

Connection NSConnectionReplyMode(Cocoa)

Cocoa使用這種類(lèi)型來(lái)監(jiān)測(cè)NSCOnnection對(duì)象返回。很少用到這種類(lèi)型。

Modal NSModalPannelRunLoopMode(Cocoa)

Cocoa使用這種類(lèi)型來(lái)標(biāo)識(shí)modal pannels相關(guān)的事件。

Event tracking NSEventTrackingRunLoopMode(Cocoa)

Cocoa使用這種類(lèi)型來(lái)監(jiān)測(cè)鼠標(biāo)拖動(dòng)以及其他類(lèi)型的用戶(hù)界面操作追蹤。

Common modes

NSRunLoopCommonModes(Cocoa)

kCFRunLoopCommonModes(Core Foundation)

這是常用類(lèi)型的集合。指定一個(gè)輸入源和這個(gè)類(lèi)型相關(guān)也就是指定它和這個(gè)集合的類(lèi)型相關(guān)。對(duì)于Cocoa程序,這個(gè)集合包括default,modal,以及event tracking類(lèi)型。Core Foundation只包括default類(lèi)型。可以使用CFRunLoopAddCommonMode方法來(lái)添加自定義類(lèi)型到這個(gè)集合

?

輸入源

輸入源異步的發(fā)送消息到線(xiàn)程。事件的源取決于輸入源,總體上有兩種類(lèi)型。基于Port的源模擬程序的port源,自定義源模擬自定義事件。不過(guò)run loop關(guān)心的并不是基于port或自定義事件。系統(tǒng)基本上兩種都會(huì)實(shí)現(xiàn)。唯一的不同是他們?nèi)绾伟l(fā)出的。基于port的是自動(dòng)由內(nèi)核發(fā)出的,自定義的源是由其他線(xiàn)程發(fā)出的。

創(chuàng)建輸入源時(shí),可以指定一個(gè)或多個(gè)run loop類(lèi)型。類(lèi)型決定了輸入源在什么時(shí)候被監(jiān)測(cè)到。大多數(shù)時(shí)候,run loop在默認(rèn)類(lèi)型下運(yùn)行,不過(guò)也可以指定類(lèi)型。如果輸入源不是當(dāng)前監(jiān)測(cè)的類(lèi)型,任何產(chǎn)生的事件都會(huì)掛起,直到有對(duì)應(yīng)的類(lèi)型能接收它。

下面介紹一些輸入源

?

Port-Based Sources

Cocoa和Core Foundation提供了創(chuàng)建port-based源的相應(yīng)對(duì)象和方法。例如,在Cocoa,基本上不用直接創(chuàng)建輸入源。只需要?jiǎng)?chuàng)建一個(gè)port對(duì)象然后使用NSPort的方法把它加到run loop中。port對(duì)象會(huì)處理創(chuàng)建和配置輸入源的事情。

?

自定義輸入源

創(chuàng)建自定義輸入源,需要使用Core Foundation中CFRunLoopSourceRef相關(guān)的方法。可以給輸入源配置幾個(gè)回調(diào)方法。Core Foundation會(huì)在不同點(diǎn)回調(diào)這些方法來(lái)配置輸入源,處理事件,以及在從run loop移除時(shí)銷(xiāo)毀它。

另外如果要定義收到事件的行為的話(huà),需要定義事件的分發(fā)機(jī)制。這部分在另一個(gè)線(xiàn)程上運(yùn)行,它負(fù)責(zé)給輸入源提供數(shù)據(jù)并且在數(shù)據(jù)準(zhǔn)備好后通知它。事件的分發(fā)之際又你決定,但是不要弄的太復(fù)雜。

?

Cocoa Perform Selector Sources

對(duì)于port-based sources,Cocoa定義了一個(gè)自定義的源來(lái)在任何線(xiàn)程上執(zhí)行方法。和port-based源相似的是,執(zhí)行方法的請(qǐng)求在目標(biāo)線(xiàn)程上被序列化,這樣可以減輕多個(gè)方法在同一個(gè)線(xiàn)程上被調(diào)用的同步問(wèn)題。和port-based源不同的是,它執(zhí)行后會(huì)從run loop把自己移除掉。

在另一個(gè)線(xiàn)程上執(zhí)行方法時(shí),目標(biāo)線(xiàn)程必須要有一個(gè)活著的run loop。對(duì)于你創(chuàng)建的線(xiàn)程,這意味著它會(huì)等到啟動(dòng)run loop時(shí)才執(zhí)行。因?yàn)橹骶€(xiàn)程會(huì)自動(dòng)啟動(dòng)它的run loop,因此在程序調(diào)用applicationDidFinishLaunching:后就可以開(kāi)始調(diào)用這個(gè)方法了。run loop會(huì)一次調(diào)用所有計(jì)劃的方法,而不是一個(gè)循環(huán)調(diào)用一個(gè)。

下面列出了NSObject在另一個(gè)線(xiàn)程上執(zhí)行方法的方法。由于是在NSObject中定義的,所以可以在任何能訪(fǎng)問(wèn)Objective-C對(duì)象的線(xiàn)程中調(diào)用,包括POSIX線(xiàn)程。這些方法不會(huì)創(chuàng)建新線(xiàn)程來(lái)執(zhí)行他們。

方法 描述

performSelectorOnMainThread:withObject:waitUntilDone:

performSelectorOnMainThread:withObject:waitUntilDone:modes:

在主線(xiàn)程的下一個(gè)run loop中執(zhí)行指定的方法。這個(gè)方法可以阻塞當(dāng)前線(xiàn)程直到指定的方法執(zhí)行完

performSelector:onThread:withObject:waitUntilDone:

performSelector:onThread:withObject:waitUntilDone:modes:

在指定的線(xiàn)程上執(zhí)行指定的方法。可以阻塞房前線(xiàn)程直到指定的方法執(zhí)行完

performSelector:withObject:afterDelay:

performSelector:withObject:afterDelay:inModes:

在當(dāng)前線(xiàn)程的下一個(gè)run loop中執(zhí)行指定方法并且可以設(shè)置延時(shí)時(shí)間。由于等到下個(gè)run loop才會(huì)執(zhí)行,所以這些方法默認(rèn)的有一個(gè)最小的延時(shí)時(shí)間。隊(duì)列中的多個(gè)方法會(huì)根據(jù)隊(duì)列中的位置一個(gè)一個(gè)的執(zhí)行。

cancelPreviousPerformRequestsWithTarget:

cancelPreviousPerformRequestsWithTarget:selector:object:

可以取消使用 performSelector:withObject:afterDelay: performSelector:withObject:afterDelay:inModes: 發(fā)送給當(dāng)前線(xiàn)程的消息。

?

定時(shí)器源

定時(shí)器源會(huì)在預(yù)設(shè)的時(shí)間給線(xiàn)程同步的發(fā)送事件。定時(shí)器是線(xiàn)程通知自己做一些事情的一種方法。例如,搜索框可以用定時(shí)器來(lái)實(shí)現(xiàn)用戶(hù)輸入內(nèi)容后自動(dòng)搜索。延時(shí)可以讓用戶(hù)在開(kāi)始搜索前輸入想輸入的內(nèi)容。

雖然它產(chǎn)生基于時(shí)間的消息,定時(shí)器并不是實(shí)時(shí)的。和輸入源一樣,定時(shí)器也是和run loop指定的類(lèi)型相關(guān)。如果定時(shí)器不在當(dāng)前run loop監(jiān)測(cè)的類(lèi)型中,它會(huì)一直等到支持定時(shí)器的run loop執(zhí)行時(shí)才會(huì)觸發(fā)。同樣的,如果定時(shí)器在run loop執(zhí)行的過(guò)程中觸發(fā)了,定時(shí)器會(huì)等到下一個(gè)run loop才能實(shí)行相應(yīng)的方法。如果run loop沒(méi)有運(yùn)行,定時(shí)器就根本不會(huì)觸發(fā)。

?

Run Loop Observers

和源不同,源是在同步或異步事件產(chǎn)生是觸發(fā),run loop observers在run loop指定的特殊點(diǎn)觸發(fā)。可以使用run loop observsers來(lái)讓線(xiàn)程準(zhǔn)備處理事件或讓線(xiàn)程準(zhǔn)備掛起。可以把run loop observers和下面的事件關(guān)聯(lián)起來(lái):

  • run loop的入口
  • run loop將要開(kāi)始執(zhí)行定時(shí)器
  • run loop將要執(zhí)行一個(gè)輸入源
  • run loop將要掛起
  • run loop被喚醒
  • run loop退出

可以使用Core Foundation來(lái)添加run loop監(jiān)聽(tīng)者。要?jiǎng)?chuàng)建一個(gè)run loop監(jiān)聽(tīng)者,需要?jiǎng)?chuàng)建一個(gè)CFRunLoopObserverRef實(shí)例。這個(gè)類(lèi)型會(huì)追蹤指定的回調(diào)方法以及感興趣的事件。

和定時(shí)器相似,run loop監(jiān)聽(tīng)者可以被使用一次或重復(fù)使用。一次性的監(jiān)聽(tīng)者會(huì)在觸發(fā)后從run loop移除,重復(fù)使用的會(huì)保留。使用一次還是重復(fù)使用是在創(chuàng)建時(shí)指定的。

?

Run Loop事件順序

每次運(yùn)行時(shí),線(xiàn)程的run loop會(huì)執(zhí)行預(yù)定義的事件并且給每個(gè)監(jiān)聽(tīng)者發(fā)廣播。調(diào)用的順序時(shí)固定的:

  1. 通知監(jiān)聽(tīng)者run loop進(jìn)入了
  2. 通知監(jiān)聽(tīng)者任何準(zhǔn)備好的定時(shí)器將要觸發(fā)
  3. 通知監(jiān)聽(tīng)者任何非基于port的輸入源將要觸發(fā)
  4. 觸發(fā)任何非基于port的事件
  5. 如果有任何基于port事件將要觸發(fā),處理事件,然后到第9步
  6. 通知監(jiān)聽(tīng)者線(xiàn)程將要掛起
  7. 把線(xiàn)程掛起直到下面的事件之一觸發(fā)
    • 一個(gè)基于port的消息觸發(fā)
    • 定義定時(shí)器觸發(fā)
    • run loop設(shè)置的超時(shí)時(shí)間到了
    • run loop被明確的喚醒
  8. 通知監(jiān)聽(tīng)者線(xiàn)程被喚醒
  9. 處理需要處理的事件
    • 如果一個(gè)用戶(hù)定義的定時(shí)器觸發(fā),執(zhí)行定時(shí)器然后重啟消息循環(huán)。跳轉(zhuǎn)到第2步
    • 如果一個(gè)事件源觸發(fā),分發(fā)事件
    • 如果run loop被明確的喚醒但是還沒(méi)有超時(shí),重啟消息循環(huán)。跳轉(zhuǎn)到第2步
  10. 通知所有的監(jiān)聽(tīng)者run loop退出

由于定時(shí)器和輸入源的監(jiān)聽(tīng)者廣播是在事件執(zhí)行前發(fā)出的,廣播的事件和真實(shí)執(zhí)行的事件可能會(huì)有時(shí)間差。如果這個(gè)時(shí)間差很重要,可以使用sleep和awake-from-sleep廣播來(lái)修正真實(shí)時(shí)間。

由于定時(shí)器和其他周期性的事件是在run loop運(yùn)行時(shí)分發(fā)的,破壞run loop會(huì)破會(huì)消息分發(fā)。

?

什么時(shí)候應(yīng)該使用Run Loop

程序中只有明確的需要使用另外一個(gè)線(xiàn)程時(shí)才會(huì)需要run loop。主線(xiàn)程的run loop是程序的基礎(chǔ)部分之一。所以,app的框架提供了運(yùn)行主線(xiàn)程以及自動(dòng)啟動(dòng)run loop的代碼。UIApplication的run方法啟動(dòng)了主循環(huán)。如果使用Xcode工程模版創(chuàng)建程序,根本步需要調(diào)用這些方法。

對(duì)于輔助線(xiàn)程,你需要決定是否需要run loop, 如果需要的話(huà)需要自己配置并啟動(dòng)它。有些時(shí)候完全步需要run loop。比如,創(chuàng)建一個(gè)線(xiàn)程來(lái)執(zhí)行一個(gè)長(zhǎng)時(shí)間或指定好的任務(wù),這時(shí)候根本不需要啟動(dòng)run loop。Run loop在需要很多線(xiàn)程間交互的時(shí)候使用。比如,需要做下面的事情時(shí):

  • 使用基于port或自定義的源來(lái)進(jìn)行線(xiàn)程間通訊
  • 在線(xiàn)程上使用定時(shí)器
  • 在Cocoa程序中使用performSelector...方法
  • 執(zhí)行周期性的任務(wù)

如果選擇使用run loop,配置和使用是相對(duì)簡(jiǎn)單的。在整個(gè)多線(xiàn)程編程過(guò)程中,最好是計(jì)劃好需要哪些輔助線(xiàn)程。線(xiàn)程最好是讓它正常結(jié)束而不是強(qiáng)行中止。

?

使用Run Loop對(duì)象

run loop對(duì)象提供了添加輸入源,定時(shí)器,run-loop obervers以及運(yùn)行它的接口。每個(gè)線(xiàn)程都有一個(gè)對(duì)應(yīng)的run loop對(duì)象。在Cocoa中,這個(gè)對(duì)象是一個(gè)NSRunLoop類(lèi)實(shí)例。在底層程序中,它是指向CFRunLoopRef的指針。

?

獲取Run Loop對(duì)象

要獲取當(dāng)前線(xiàn)程的run loop對(duì)象,可以使用下面的方法:

  • 在Cocoa程序中,使用NSRunLoop的currentRunLoop方法來(lái)獲得NSRunLoop對(duì)象。
  • 使用CFRunLoopGetCurrent方法。

雖然他們不是完全相同的,但是也可以通過(guò)NSRunLoop對(duì)象獲得CFRunLoopRef。NSRunLoop類(lèi)定義了一個(gè)getCGRunLoop方法來(lái)返回一個(gè)CGRunLoopRef類(lèi)型。由于兩種對(duì)象指向同一個(gè)run loop,因此NSRunLoop對(duì)象和CFRunLoopRef可以混合使用。

?

配置Run Loop

在輔助線(xiàn)程中使用run loop之前,需要至少加入一個(gè)輸入源或定時(shí)器。如果run loop沒(méi)有東西要監(jiān)控,它運(yùn)行時(shí)會(huì)立刻退出。

除了添加源以外,也可以添加run loop監(jiān)聽(tīng)者來(lái)監(jiān)測(cè)run loop運(yùn)行的狀態(tài)。創(chuàng)建run loop監(jiān)聽(tīng)者,需要?jiǎng)?chuàng)建一個(gè)CFRunLoopObserverRef類(lèi)型,然后使用CGRunLoopAddObserver方法添加到run loop。就算是Cocoa程序,run loop監(jiān)聽(tīng)者也需要使用Core Foundation來(lái)創(chuàng)建。

下面展示了創(chuàng)建run loop監(jiān)聽(tīng)者的主要程序。

      - (
      
        void
      
      
        )threadMain

{

    
      
      
        //
      
      
         The application uses garbage collection, so no autorelease pool is needed.
      
      

    NSRunLoop* myRunLoop =
      
         [NSRunLoop currentRunLoop];

 

    
      
      
        //
      
      
         Create a run loop observer and attach it to the run loop.
      
      

    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);

    }

 

    
      
      
        //
      
      
         Create and schedule the timer.
      
      

    [NSTimer scheduledTimerWithTimeInterval:
      
        0.1
      
      
         target:self

                selector:@selector(doFireTimer:) userInfo:nil repeats:YES];

 

    NSInteger    loopCount 
      
      = 
      
        10
      
      
        ;

    
      
      
        do
      
      
        

    {

        
      
      
        //
      
      
         Run the run loop 10 times to let the timer fire.
      
      

        [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:
      
        1
      
      
        ]];

        loopCount
      
      --
      
        ;

    }

    
      
      
        while
      
      
         (loopCount);

}
      
    

如果是配置長(zhǎng)時(shí)間存活的線(xiàn)程,最好是添加一個(gè)輸入源來(lái)接收消息。雖然可以只添加一個(gè)定時(shí)器,一旦定時(shí)器觸發(fā)后,如果定時(shí)器無(wú)效了,就會(huì)導(dǎo)致run loop退出。添加重復(fù)的定時(shí)器可以讓run loop一直運(yùn)行,但是會(huì)周期性的喚醒線(xiàn)程。相對(duì)來(lái)說(shuō),輸入源會(huì)等待事件發(fā)生,直到事件發(fā)生時(shí)才喚醒線(xiàn)程。

?

啟動(dòng)Run Loop

只有輔助線(xiàn)程才需要我們啟動(dòng)run loop。run loop必須要有一個(gè)輸入源或定時(shí)器。如果沒(méi)有,run loop會(huì)立刻退出。

有幾種方式啟動(dòng)run loop,包括:

  • 無(wú)條件的
  • 設(shè)置一個(gè)時(shí)間限制
  • 指定一個(gè)類(lèi)型

無(wú)條件的進(jìn)入run loop是最簡(jiǎn)單的,同時(shí)也是最不推薦的。無(wú)條件的啟動(dòng)run loop會(huì)讓線(xiàn)程進(jìn)入一個(gè)死循環(huán),會(huì)讓你基本無(wú)法控制run loop。可以添加刪除輸入源和定時(shí)器,但是想要停止run loop的方法只能強(qiáng)行殺掉它。

相對(duì)于無(wú)條件的運(yùn)行,更好的方式是設(shè)置一個(gè)時(shí)間限制。設(shè)置一個(gè)時(shí)間限制后,run loop會(huì)運(yùn)行到有事件觸發(fā)活著到達(dá)設(shè)置的時(shí)間。如果有事件觸發(fā),會(huì)分發(fā)事件然后退出run loop。你可以在代碼中重啟run loop來(lái)等待下一個(gè)事件。如果設(shè)置的時(shí)間到了,可以見(jiàn)到的重啟run loop或使用這個(gè)時(shí)間做點(diǎn)其他事。

除了設(shè)置時(shí)間限制外,也可以給run loop指定一種類(lèi)型。類(lèi)型和超時(shí)并不是互斥的,他們可以同時(shí)被使用。類(lèi)型用來(lái)限制輸入源的類(lèi)型。

下面展示一個(gè)線(xiàn)程的入口框架。主要是添加輸入源和定時(shí)器后,重復(fù)的調(diào)用run loop來(lái)接收消息。每次run loop返回時(shí),查看是否到達(dá)了結(jié)束的條件。

      - (
      
        void
      
      
        )skeletonThreadMain

{

    
      
      
        //
      
      
         Set up an autorelease pool here if not using garbage collection.
      
      

    BOOL done =
      
         NO;

 

    
      
      
        //
      
      
         Add your sources or timers to the run loop and do any other setup.
      
      
        do
      
      
        

    {

        
      
      
        //
      
      
         Start the run loop but return after each source is handled.
      
      

        SInt32    result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 
      
        10
      
      
        , YES);

 

        
      
      
        //
      
      
         If a source explicitly stopped the run loop, or if there are no

        
      
      
        //
      
      
         sources or timers, go ahead and exit.
      
      
        if
      
       ((result == kCFRunLoopRunStopped) || (result ==
      
         kCFRunLoopRunFinished))

            done 
      
      =
      
         YES;

 

        
      
      
        //
      
      
         Check for any other exit conditions here and set the

        
      
      
        //
      
      
         done variable as needed.
      
      
            }

    
      
      
        while
      
       (!
      
        done);

 

    
      
      
        //
      
      
         Clean up code here. Be sure to release any allocated autorelease pools.
      
      

}
    

run loop是可以遞歸的使用的。也就是說(shuō),可以在輸入源或定時(shí)器的處理程序中調(diào)用CFRunLoopRun,CFRunLoopRunInMode等方法。

?

退出Run Loop

有兩種方式可以讓run loop收到事件前退出

  • 配置run loop的超時(shí)時(shí)間
  • 主動(dòng)讓run loop停止

如果可以的話(huà),最好是使用超時(shí)時(shí)間。指定超時(shí)時(shí)間可以讓run loop在退出前完成它所有應(yīng)該做的事,包括給監(jiān)聽(tīng)者發(fā)送廣播。

明確的使用CFRunLoopStop停止run loop和超時(shí)相似。run loop也會(huì)把需要發(fā)送的廣播發(fā)送給監(jiān)聽(tīng)者。不同的事這種方法主要用在使用無(wú)條件啟動(dòng)的run loop。

雖然刪除Run Loop的輸入源和定時(shí)器也會(huì)導(dǎo)致run loop退出,但是不是很靠譜。系統(tǒng)也許會(huì)自動(dòng)添加一些輸入源。可能代碼沒(méi)有意識(shí)到這些輸入源,他們可能是無(wú)法移除的,這會(huì)導(dǎo)致run loop無(wú)法退出。

?

線(xiàn)程安全以及Run Loop對(duì)象

線(xiàn)程安全取決于你使用什么API來(lái)維護(hù)run loop。Core Foundation中的方法整體上都是線(xiàn)程安全的并且可以在任何線(xiàn)程調(diào)用。如果是做改變r(jià)un loop的配置的操作,最好還是在run loop所屬的線(xiàn)程做比較好。

Cocoa的NSRunLoop類(lèi)并沒(méi)有繼承Core Foundation的線(xiàn)程安全部分。如果是使用NSRunLoop類(lèi)來(lái)修改run loop,應(yīng)該在run loop所屬的線(xiàn)程上做。在另一個(gè)線(xiàn)程上給run loop添加輸入源或定時(shí)器可能會(huì)導(dǎo)致crash或其他異常。

?

配置Run Loop源

自定義輸入源

創(chuàng)建自定義源包括下面一些內(nèi)容:

  • 輸入源需要執(zhí)行的信息
  • 感興趣的客戶(hù)如何與輸入源交互
  • 處理客戶(hù)請(qǐng)求的處理程序
  • 取消輸入源的方法
  • 由于是自定義的輸入源來(lái)處理自定義的信息,所有實(shí)際的配置就很靈活了。調(diào)度,處理,取消流程是自定義源的主要流程。大多數(shù)其他行為都在這幾個(gè)方法之外。比如,什么時(shí)候傳遞數(shù)據(jù)什么時(shí)候和其他線(xiàn)程交互由你決定。

下面的圖展示了一個(gè)簡(jiǎn)單的自定義源。程序的主線(xiàn)程維護(hù)了一個(gè)輸入源的引用,輸入源的命令緩沖區(qū),以及輸入源所在的run loop。當(dāng)主線(xiàn)程有工作交給工作線(xiàn)程時(shí),它把命令和所需的數(shù)據(jù)一起發(fā)到命令緩沖區(qū)來(lái)讓工作線(xiàn)程開(kāi)始工作。(由于主線(xiàn)程和工作線(xiàn)程都能訪(fǎng)問(wèn)命令緩沖區(qū),所以訪(fǎng)問(wèn)必須是同步的。)命令發(fā)送之后,主線(xiàn)程會(huì)給輸入源發(fā)送一個(gè)信號(hào)來(lái)喚醒工作線(xiàn)程的run loop。當(dāng)收到喚醒命令后,run loop調(diào)用輸入源的處理程序來(lái)處理命令緩沖區(qū)中的命令。

ios 多線(xiàn)程開(kāi)發(fā)(三)Run Loops

?

定義輸入源

定義輸入源需要使用Core Foundation來(lái)配置以及和run loop交互。雖然基本的處理程序是基于C的方法,也可以使用Objective-C或C++來(lái)封裝。

下面展示了一個(gè)輸入源的定義。RunLoopSource對(duì)象管理一個(gè)命令緩沖區(qū)并且用它來(lái)接收其他線(xiàn)程的消息。也展示了一個(gè)RunLoopContext對(duì)象的定義,它只是一個(gè)把RunLoopSource對(duì)象和run loop對(duì)象的引用傳遞給主線(xiàn)程的容器。

      
        @interface
      
      
         RunLoopSource : NSObject

{

    CFRunLoopSourceRef runLoopSource;

    NSMutableArray
      
      *
      
         commands;

}

 


      
      - (
      
        id
      
      
        )init;


      
      - (
      
        void
      
      
        )addToCurrentRunLoop;


      
      - (
      
        void
      
      
        )invalidate;

 


      
      
        //
      
      
         Handler method
      
      

- (
      
        void
      
      
        )sourceFired;

 


      
      
        //
      
      
         Client interface for registering commands to process
      
      

- (
      
        void
      
      )addCommand:(NSInteger)command withData:(
      
        id
      
      
        )data;


      
      - (
      
        void
      
      
        )fireAllCommandsOnRunLoop:(CFRunLoopRef)runloop;

 


      
      
        @end
      
      
        //
      
      
         These are the CFRunLoopSourceRef callback functions.
      
      
        void
      
       RunLoopSourceScheduleRoutine (
      
        void
      
       *
      
        info, CFRunLoopRef rl, CFStringRef mode);


      
      
        void
      
       RunLoopSourcePerformRoutine (
      
        void
      
       *
      
        info);


      
      
        void
      
       RunLoopSourceCancelRoutine (
      
        void
      
       *
      
        info, CFRunLoopRef rl, CFStringRef mode);

 


      
      
        //
      
      
         RunLoopContext is a container object used during registration of the input source.
      
      
        @interface
      
      
         RunLoopContext : NSObject

{

    CFRunLoopRef        runLoop;

    RunLoopSource
      
      *
      
                source;

}

@property (
      
      
        readonly
      
      
        ) CFRunLoopRef runLoop;

@property (
      
      
        readonly
      
      ) RunLoopSource*
      
         source;

 


      
      - (
      
        id
      
      )initWithSource:(RunLoopSource*
      
        )src andLoop:(CFRunLoopRef)loop;


      
      
        @end
      
    

雖然使用Objectice-C代碼管理輸入源的自定義數(shù)據(jù),把輸入源和run loop關(guān)聯(lián)仍然需要c回調(diào)方法。第一個(gè)被調(diào)用的方法是把它和run loop關(guān)聯(lián)起來(lái)。由于這個(gè)輸入源只有一個(gè)客戶(hù)端(主線(xiàn)程),它調(diào)用計(jì)劃方來(lái)發(fā)送消息來(lái)注冊(cè)到線(xiàn)程上。當(dāng)代理需要和輸入源通訊時(shí),使用RunLoopContext對(duì)象中的信息就可以了。

      
        void
      
       RunLoopSourceScheduleRoutine (
      
        void
      
       *
      
        info, CFRunLoopRef rl, CFStringRef mode)

{

    RunLoopSource
      
      * obj = (RunLoopSource*
      
        )info;

    AppDelegate
      
      *   del =
      
         [AppDelegate sharedAppDelegate];

    RunLoopContext
      
      * theContext =
      
         [[RunLoopContext alloc] initWithSource:obj andLoop:rl];

 

    [del performSelectorOnMainThread:@selector(registerSource:)

                                withObject:theContext waitUntilDone:NO];

}
      
    

最重要的回調(diào)過(guò)程之一是,當(dāng)輸入源觸發(fā)時(shí)處理自定義的數(shù)據(jù)。下面展示了處理RunLoopSource對(duì)象相關(guān)的回調(diào)。這個(gè)方法只是簡(jiǎn)單的把請(qǐng)求轉(zhuǎn)發(fā)給sourceFired方法,它會(huì)執(zhí)行命令緩沖區(qū)中的命令。

      
        void
      
       RunLoopSourcePerformRoutine (
      
        void
      
       *
      
        info)

{

    RunLoopSource
      
      *  obj = (RunLoopSource*
      
        )info;

    [obj sourceFired];

}
      
    

如果你有調(diào)用CFRunLoopSourceInvalidate方法來(lái)移除輸入源,系統(tǒng)會(huì)調(diào)用輸入源的取消操作。可以在這個(gè)時(shí)候通知客戶(hù)端將要無(wú)效,客戶(hù)端需要移除對(duì)它的引用。下面展示了RunLoopSource對(duì)象注冊(cè)的取消回調(diào)方法。這個(gè)方法發(fā)送RunLoopContext對(duì)象給程序的代理,這次時(shí)通知他們移除引用

      
        void
      
       RunLoopSourceCancelRoutine (
      
        void
      
       *
      
        info, CFRunLoopRef rl, CFStringRef mode)

{

    RunLoopSource
      
      * obj = (RunLoopSource*
      
        )info;

    AppDelegate
      
      * del =
      
         [AppDelegate sharedAppDelegate];

    RunLoopContext
      
      * theContext =
      
         [[RunLoopContext alloc] initWithSource:obj andLoop:rl];

 

    [del performSelectorOnMainThread:@selector(removeSource:)

                                withObject:theContext waitUntilDone:YES];

}
      
    

?

在Run Loop上配置輸入源

下面是RunLoopSource類(lèi)的init和addToCurrentRunLoop方法。init方法創(chuàng)建一個(gè)CGRunLoopSourceRef類(lèi)型是實(shí)際被關(guān)聯(lián)到run loop的。它返回它自己也就是RunLoopSource對(duì)象,這樣外面可以有一個(gè)指向?qū)ο蟮闹羔槨L砑拥骄€(xiàn)程的工作直到工作線(xiàn)程調(diào)用addToCurrentRUnLoop方法后才會(huì)生效,那時(shí)候RunLoopSourceScheduleRoutine回調(diào)方法會(huì)被調(diào)用。只要這個(gè)源加到run loop上之后,線(xiàn)程就可以運(yùn)行run loop來(lái)等待消息了

      - (
      
        id
      
      
        )init

{

    CFRunLoopSourceContext    context 
      
      = {
      
        0
      
      
        , self, NULL, NULL, NULL, NULL, NULL,

                                        
      
      &
      
        RunLoopSourceScheduleRoutine,

                                        RunLoopSourceCancelRoutine,

                                        RunLoopSourcePerformRoutine};

 

    runLoopSource 
      
      = CFRunLoopSourceCreate(NULL, 
      
        0
      
      , &
      
        context);

    commands 
      
      =
      
         [[NSMutableArray alloc] init];

 

    
      
      
        return
      
      
         self;

}

 


      
      - (
      
        void
      
      
        )addToCurrentRunLoop

{

    CFRunLoopRef runLoop 
      
      =
      
         CFRunLoopGetCurrent();

    CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopDefaultMode);

}
      
    

?

客戶(hù)端和輸入源對(duì)應(yīng)

想要輸入源真的有用的話(huà),需要維護(hù)它并且用它向其他線(xiàn)程發(fā)送信號(hào)。輸入源的主要功能是讓相關(guān)的線(xiàn)程掛起,直到他們有事要做的時(shí)候在喚醒。所以就需要讓其他線(xiàn)程直到這個(gè)輸入源并且有一個(gè)和它通訊的方式。

一種讓客戶(hù)端知道輸入源的方法是在第一個(gè)裝載在run loop上時(shí)發(fā)送注冊(cè)請(qǐng)求。可以注冊(cè)任意多的你想要的客戶(hù)端,或者這冊(cè)一個(gè)核心的,然后由它把消息轉(zhuǎn)給其他的。下面展示一個(gè)定義在程序回調(diào)中的注冊(cè)方法(它在RunLoopSource對(duì)象的計(jì)劃方法中被調(diào)用了)。這個(gè)方法接收到一個(gè)RunLoopContext對(duì)象然后加入到列表中。這里也展示了如何注銷(xiāo)它

      - (
      
        void
      
      )registerSource:(RunLoopContext*
      
        )sourceInfo;

{

    [sourcesToPing addObject:sourceInfo];

}

 


      
      - (
      
        void
      
      )removeSource:(RunLoopContext*
      
        )sourceInfo

{

    
      
      
        id
      
          objToRemove =
      
         nil;

 

    
      
      
        for
      
       (RunLoopContext* context 
      
        in
      
      
         sourcesToPing)

    {

        
      
      
        if
      
      
         ([context isEqual:sourceInfo])

        {

            objToRemove 
      
      =
      
         context;

            
      
      
        break
      
      
        ;

        }

    }

 

    
      
      
        if
      
      
         (objToRemove)

        [sourcesToPing removeObject:objToRemove];

}
      
    

?

輸入源發(fā)送信號(hào)

在處理好輸入源的數(shù)據(jù)后,客戶(hù)端就可以發(fā)送信號(hào)來(lái)喚醒run loop了。輸入源發(fā)信號(hào)可以讓run loop知道可以準(zhǔn)備好執(zhí)行了。因?yàn)橛锌赡茉谑盏叫盘?hào)時(shí)線(xiàn)程是掛起狀態(tài),所以每次都需要明確的喚醒run loop。否則可能會(huì)導(dǎo)致輸入源延遲執(zhí)行。

下面展示了RunLoopSource的fireCommandsOnRunLoop方法。客戶(hù)端把命令加入到命令緩沖區(qū)并且準(zhǔn)備好執(zhí)行后會(huì)調(diào)用它。

      - (
      
        void
      
      
        )fireCommandsOnRunLoop:(CFRunLoopRef)runloop

{

    CFRunLoopSourceSignal(runLoopSource);

    CFRunLoopWakeUp(runloop);

}
      
    

注意:不要嘗試去處理SIGHUP或其他線(xiàn)程級(jí)別的信號(hào)。Core Foundation喚醒run loop的信號(hào)不是信號(hào)安全的,不應(yīng)該在你的程序中處理。

?

配置定時(shí)器源

要?jiǎng)?chuàng)建一個(gè)定時(shí)器源,只需要?jiǎng)?chuàng)建一個(gè)定時(shí)器對(duì)象然后加到run loop上。在Cocoa中,可以使用NSTimer類(lèi)來(lái)創(chuàng)建定時(shí)器對(duì)象,在Core Foundation中可以使用CFRunLoopTimerRef。實(shí)際上NSTimer類(lèi)是Core Foundation的擴(kuò)展,提供了一下更方便的方法,比如創(chuàng)建以及添加到線(xiàn)程。

在Cocoa中,可以通過(guò)下面的方法創(chuàng)建定時(shí)器

  • scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
  • scheduledTimerWithTimeInterval:invocation:repeats:

這些方法會(huì)創(chuàng)建一個(gè)定時(shí)器然后以默認(rèn)類(lèi)型(NSDefaultRunLoopMode)添加到當(dāng)前線(xiàn)程的run loop。也可以手動(dòng)創(chuàng)建一個(gè)NSTImer對(duì)象,然后調(diào)用NSRunLoop的addTimer:forMode:方法添加到run loop上。兩種方法本質(zhì)上做了相同的事,但是可以讓你在不同層面上控制定時(shí)器的配置。比如,如果手動(dòng)的創(chuàng)建定時(shí)器然后添加到run loop,這樣可以使用除了默認(rèn)類(lèi)型以外的類(lèi)型。下面展示了兩種方法。第一個(gè)定時(shí)器延時(shí)1秒后每0.1秒觸發(fā)一次。第二個(gè)定時(shí)器0.2秒后每0.2秒觸發(fā)一次。

      NSRunLoop* myRunLoop =
      
         [NSRunLoop currentRunLoop];

 


      
      
        //
      
      
         Create and schedule the first timer.
      
      

NSDate* futureDate = [NSDate dateWithTimeIntervalSinceNow:
      
        1.0
      
      
        ];

NSTimer
      
      * myTimer =
      
         [[NSTimer alloc] initWithFireDate:futureDate

                        interval:
      
      
        0.1
      
      
        

                        target:self

                        selector:@selector(myDoFireTimer1:)

                        userInfo:nil

                        repeats:YES];

[myRunLoop addTimer:myTimer forMode:NSDefaultRunLoopMode];

 


      
      
        //
      
      
         Create and schedule the second timer.
      
      

[NSTimer scheduledTimerWithTimeInterval:
      
        0.2
      
      
        

                        target:self

                        selector:@selector(myDoFireTimer2:)

                        userInfo:nil

                        repeats:YES];
      
    

下面展示了使用Core Foundation方法來(lái)配置定時(shí)器。雖然下面沒(méi)有傳遞任何用戶(hù)定義的信息,但是你可以使用這個(gè)數(shù)據(jù)結(jié)構(gòu)傳遞你想傳遞的任何數(shù)據(jù)

      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);
      
    

?

配置Port-Based輸入源

Cocoa和Core Foundation都提供了port-based對(duì)象來(lái)進(jìn)行線(xiàn)程間通訊。

?

配置NSMachPort對(duì)象

要建立和NSMachPort對(duì)象的聯(lián)系,需要?jiǎng)?chuàng)建一個(gè)port對(duì)象然后把它加到線(xiàn)程的run loop中。當(dāng)啟動(dòng)輔助線(xiàn)程時(shí),把同樣的對(duì)象傳遞給線(xiàn)程的入口方法。輔助線(xiàn)程就可以使用相同的對(duì)象來(lái)把消息發(fā)送回來(lái)。

?

主線(xiàn)程代碼實(shí)現(xiàn)

下面展示了啟動(dòng)輔助線(xiàn)程的代碼。Cocoa框架處理了port和run loop的一些中間步驟,所有啟動(dòng)線(xiàn)程的方法比Core Fouundation要短。但是兩種效果是一樣的。不同的是這個(gè)方法直接發(fā)送NSPort對(duì)象給工作線(xiàn)程

      - (
      
        void
      
      
        )launchThread

{

    NSPort
      
      * myPort =
      
         [NSMachPort port];

    
      
      
        if
      
      
         (myPort)

    {

        
      
      
        //
      
      
         This class handles incoming port messages.
      
      
                [myPort setDelegate:self];

 

        
      
      
        //
      
      
         Install the port as an input source on the current run loop.
      
      
                [[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode];

 

        
      
      
        //
      
      
         Detach the thread. Let the worker release the port.
      
      
                [NSThread detachNewThreadSelector:@selector(LaunchThreadWithPort:)

               toTarget:[MyWorkerClass 
      
      
        class
      
      
        ] withObject:myPort];

    }

}
      
    

如果要設(shè)置線(xiàn)程的雙向通訊,也需要工作線(xiàn)程把它的port發(fā)送給主線(xiàn)程。

      
        #define
      
       kCheckinMessage 100

 


      
        //
      
      
         Handle responses from the worker thread.
      
      

- (
      
        void
      
      )handlePortMessage:(NSPortMessage *
      
        )portMessage

{

    unsigned 
      
      
        int
      
       message =
      
         [portMessage msgid];

    NSPort
      
      * distantPort =
      
         nil;

 

    
      
      
        if
      
       (message ==
      
         kCheckinMessage)

    {

        
      
      
        //
      
      
         Get the worker thread’s communications port.
      
      

        distantPort =
      
         [portMessage sendPort];

 

        
      
      
        //
      
      
         Retain and save the worker port for later use.
      
      
                [self storeDistantPort:distantPort];

    }

    
      
      
        else
      
      
        

    {

        
      
      
        //
      
      
         Handle other messages.
      
      
            }

}
      
    

輔助線(xiàn)程代碼實(shí)現(xiàn)

對(duì)于輔助線(xiàn)程,需要配置線(xiàn)程來(lái)使用port和主線(xiàn)程通訊。

下面講述了設(shè)置輔助線(xiàn)程。在創(chuàng)建autorelease pool之后,它創(chuàng)建了一個(gè)工作對(duì)象來(lái)控制線(xiàn)程執(zhí)行。工作對(duì)象的sendCheckinMessage:方法給工作線(xiàn)程創(chuàng)建一個(gè)本地port然后發(fā)送消息給主線(xiàn)程。

      +(
      
        void
      
      )LaunchThreadWithPort:(
      
        id
      
      
        )inData

{

    NSAutoreleasePool
      
      *  pool =
      
         [[NSAutoreleasePool alloc] init];

 

    
      
      
        //
      
      
         Set up the connection between this thread and the main thread.
      
      

    NSPort* distantPort = (NSPort*
      
        )inData;

 

    MyWorkerClass
      
      *  workerObj =
      
         [[self alloc] init];

    [workerObj sendCheckinMessage:distantPort];

    [distantPort release];

 

    
      
      
        //
      
      
         Let the run loop process things.
      
      
        do
      
      
        

    {

        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode

                            beforeDate:[NSDate distantFuture]];

    }

    
      
      
        while
      
       (!
      
        [workerObj shouldExit]);

 

    [workerObj release];

    [pool release];

}
      
    

在使用NSMachPort時(shí),線(xiàn)程間單向通訊可以使用同一個(gè)對(duì)象。也就是說(shuō),當(dāng)前線(xiàn)程創(chuàng)建的port對(duì)象是其他線(xiàn)程接收到的port對(duì)象。

下面是輔助線(xiàn)程的check-in流程。這個(gè)方法設(shè)置了一個(gè)port用來(lái)在以后進(jìn)行通訊,然后把它發(fā)送給主線(xiàn)程。這個(gè)方法使用LaunchThreadWithPort:方法傳過(guò)來(lái)的port對(duì)象

      
        //
      
      
         Worker thread check-in method
      
      

- (
      
        void
      
      )sendCheckinMessage:(NSPort*
      
        )outPort

{

    
      
      
        //
      
      
         Retain and save the remote port for future use.
      
      
            [self setRemotePort:outPort];

 

    
      
      
        //
      
      
         Create and configure the worker thread port.
      
      

    NSPort* myPort =
      
         [NSMachPort port];

    [myPort setDelegate:self];

    [[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode];

 

    
      
      
        //
      
      
         Create the check-in message.
      
      

    NSPortMessage* messageObj =
      
         [[NSPortMessage alloc] initWithSendPort:outPort

                                         receivePort:myPort components:nil];

 

    
      
      
        if
      
      
         (messageObj)

    {

        
      
      
        //
      
      
         Finish configuring the message and send it immediately.
      
      
                [messageObj setMsgId:setMsgid:kCheckinMessage];

        [messageObj sendBeforeDate:[NSDate date]];

    }

}
      
    

配置NSMessagePort對(duì)象

與NSMessagePort對(duì)象建立連接并不是見(jiàn)到的在線(xiàn)程間傳遞port對(duì)象。遠(yuǎn)程port纖細(xì)必須要有一個(gè)名字。Cocoa用一個(gè)特定的名字注冊(cè)port然后把它傳遞給遠(yuǎn)程線(xiàn)程來(lái)進(jìn)行通訊。下面展示了創(chuàng)建和注冊(cè)消息port的代碼

      NSPort* localPort =
      
         [[NSMessagePort alloc] init];

 


      
      
        //
      
      
         Configure the object and add it to the current run loop.
      
      
        [localPort setDelegate:self];

[[NSRunLoop currentRunLoop] addPort:localPort forMode:NSDefaultRunLoopMode];

 


      
      
        //
      
      
         Register the port using a specific name. The name must be unique.
      
      

NSString* localPortName = [NSString stringWithFormat:
      
        @"
      
      
        MyPortName
      
      
        "
      
      
        ];

[[NSMessagePortNameServer sharedInstance] registerPort:localPort

                     name:localPortName];
      
    

使用Core Foundation配置port-based輸入源

這里展示如何使用Core Foundation在主線(xiàn)程和工作線(xiàn)程之間設(shè)置一個(gè)雙向通訊通道。

主線(xiàn)程調(diào)用下面的方法來(lái)啟動(dòng)工作線(xiàn)程。里面做的第一件事是設(shè)置了一個(gè)CFMessagePortRef類(lèi)型來(lái)監(jiān)聽(tīng)工作線(xiàn)程的消息。工作線(xiàn)程需要port的名字來(lái)建立連接, 所以名字會(huì)在工作線(xiàn)程的入口傳過(guò)去。名字必須是唯一的。

      
        #define
      
       kThreadStackSize        (8 *4096)
      
        

 

OSStatus MySpawnThread()

{

    
      
      
        //
      
      
         Create a local port for receiving responses.
      
      
            CFStringRef myPortName;

    CFMessagePortRef myPort;

    CFRunLoopSourceRef rlSource;

    CFMessagePortContext context 
      
      = {
      
        0
      
      
        , NULL, NULL, NULL, NULL};

    Boolean shouldFreeInfo;

 

    
      
      
        //
      
      
         Create a string with the port name.
      
      

    myPortName = CFStringCreateWithFormat(NULL, NULL, CFSTR(
      
        "
      
      
        com.myapp.MainThread
      
      
        "
      
      
        ));

 

    
      
      
        //
      
      
         Create the port.
      
      

    myPort =
      
         CFMessagePortCreateLocal(NULL,

                myPortName,

                
      
      &
      
        MainThreadResponseHandler,

                
      
      &
      
        context,

                
      
      &
      
        shouldFreeInfo);

 

    
      
      
        if
      
       (myPort !=
      
         NULL)

    {

        
      
      
        //
      
      
         The port was successfully created.

        
      
      
        //
      
      
         Now create a run loop source for it.
      
      

        rlSource = CFMessagePortCreateRunLoopSource(NULL, myPort, 
      
        0
      
      
        );

 

        
      
      
        if
      
      
         (rlSource)

        {

            
      
      
        //
      
      
         Add the source to the current run loop.
      
      
                    CFRunLoopAddSource(CFRunLoopGetCurrent(), rlSource, kCFRunLoopDefaultMode);

 

            
      
      
        //
      
      
         Once installed, these can be freed.
      
      
                    CFRelease(myPort);

            CFRelease(rlSource);

        }

    }

 

    
      
      
        //
      
      
         Create the thread and continue processing.
      
      
            MPTaskID        taskID;

    
      
      
        return
      
      (MPCreateTask(&
      
        ServerThreadEntryPoint,

                    (
      
      
        void
      
      *
      
        )myPortName,

                    kThreadStackSize,

                    NULL,

                    NULL,

                    NULL,

                    
      
      
        0
      
      
        ,

                    
      
      &
      
        taskID));

}
      
    

線(xiàn)程啟動(dòng)之后,主線(xiàn)程在等待反饋時(shí)會(huì)繼續(xù)執(zhí)行其他的任務(wù)。當(dāng)反饋消息回來(lái)時(shí),會(huì)分發(fā)到下面的MainThreadResponseHandler方法。

      
        #define
      
       kCheckinMessage 100

 


      
        //
      
      
         Main thread port message handler
      
      
        CFDataRef MainThreadResponseHandler(CFMessagePortRef local,

                    SInt32 msgid,

                    CFDataRef data,

                    
      
      
        void
      
      *
      
         info)

{

    
      
      
        if
      
       (msgid ==
      
         kCheckinMessage)

    {

        CFMessagePortRef messagePort;

        CFStringRef threadPortName;

        CFIndex bufferLength 
      
      =
      
         CFDataGetLength(data);

        UInt8
      
      * buffer = CFAllocatorAllocate(NULL, bufferLength, 
      
        0
      
      
        );

 

        CFDataGetBytes(data, CFRangeMake(
      
      
        0
      
      
        , bufferLength), buffer);

        threadPortName 
      
      =
      
         CFStringCreateWithBytes (NULL, buffer, bufferLength, kCFStringEncodingASCII, FALSE);

 

        
      
      
        //
      
      
         You must obtain a remote message port by name.
      
      

        messagePort =
      
         CFMessagePortCreateRemote(NULL, (CFStringRef)threadPortName);

 

        
      
      
        if
      
      
         (messagePort)

        {

            
      
      
        //
      
      
         Retain and save the thread’s comm port for future reference.
      
      
                    AddPortToListOfActiveThreads(messagePort);

 

            
      
      
        //
      
      
         Since the port is retained by the previous function, release

            
      
      
        //
      
      
         it here.
      
      
                    CFRelease(messagePort);

        }

 

        
      
      
        //
      
      
         Clean up.
      
      
                CFRelease(threadPortName);

        CFAllocatorDeallocate(NULL, buffer);

    }

    
      
      
        else
      
      
        

    {

        
      
      
        //
      
      
         Process other messages.
      
      
            }

 

    
      
      
        return
      
      
         NULL;

}
      
    

主線(xiàn)程配置好之后,剩下的工作是給工作線(xiàn)程創(chuàng)建port。下面展示了工作線(xiàn)程的入口。

      OSStatus ServerThreadEntryPoint(
      
        void
      
      *
      
         param)

{

    
      
      
        //
      
      
         Create the remote port to the main thread.
      
      
            CFMessagePortRef mainThreadPort;

    CFStringRef portName 
      
      =
      
         (CFStringRef)param;

 

    mainThreadPort 
      
      =
      
         CFMessagePortCreateRemote(NULL, portName);

 

    
      
      
        //
      
      
         Free the string that was passed in param.
      
      
            CFRelease(portName);

 

    
      
      
        //
      
      
         Create a port for the worker thread.
      
      

    CFStringRef myPortName = CFStringCreateWithFormat(NULL, NULL, CFSTR(
      
        "
      
      
        com.MyApp.Thread-%d
      
      
        "
      
      
        ), MPCurrentTaskID());

 

    
      
      
        //
      
      
         Store the port in this thread’s context info for later reference.
      
      

    CFMessagePortContext context = {
      
        0
      
      
        , mainThreadPort, NULL, NULL, NULL};

    Boolean shouldFreeInfo;

    Boolean shouldAbort 
      
      =
      
         TRUE;

 

    CFMessagePortRef myPort 
      
      =
      
         CFMessagePortCreateLocal(NULL,

                myPortName,

                
      
      &
      
        ProcessClientRequest,

                
      
      &
      
        context,

                
      
      &
      
        shouldFreeInfo);

 

    
      
      
        if
      
      
         (shouldFreeInfo)

    {

        
      
      
        //
      
      
         Couldn't create a local port, so kill the thread.
      
      

        MPExit(
      
        0
      
      
        );

    }

 

    CFRunLoopSourceRef rlSource 
      
      = CFMessagePortCreateRunLoopSource(NULL, myPort, 
      
        0
      
      
        );

    
      
      
        if
      
       (!
      
        rlSource)

    {

        
      
      
        //
      
      
         Couldn't create a local port, so kill the thread.
      
      

        MPExit(
      
        0
      
      
        );

    }

 

    
      
      
        //
      
      
         Add the source to the current run loop.
      
      
            CFRunLoopAddSource(CFRunLoopGetCurrent(), rlSource, kCFRunLoopDefaultMode);

 

    
      
      
        //
      
      
         Once installed, these can be freed.
      
      
            CFRelease(myPort);

    CFRelease(rlSource);

 

    
      
      
        //
      
      
         Package up the port name and send the check-in message.
      
      

    CFDataRef returnData =
      
         nil;

    CFDataRef outData;

    CFIndex stringLength 
      
      =
      
         CFStringGetLength(myPortName);

    UInt8
      
      * buffer = CFAllocatorAllocate(NULL, stringLength, 
      
        0
      
      
        );

 

    CFStringGetBytes(myPortName,

                CFRangeMake(
      
      
        0
      
      
        ,stringLength),

                kCFStringEncodingASCII,

                
      
      
        0
      
      
        ,

                FALSE,

                buffer,

                stringLength,

                NULL);

 

    outData 
      
      =
      
         CFDataCreate(NULL, buffer, stringLength);

 

    CFMessagePortSendRequest(mainThreadPort, kCheckinMessage, outData, 
      
      
        0.1
      
      , 
      
        0.0
      
      
        , NULL, NULL);

 

    
      
      
        //
      
      
         Clean up thread data structures.
      
      
            CFRelease(outData);

    CFAllocatorDeallocate(NULL, buffer);

 

    
      
      
        //
      
      
         Enter the run loop.
      
      
            CFRunLoopRun();

}
      
    

進(jìn)入run loop后,其他發(fā)送過(guò)來(lái)的時(shí)間由ProcessClientRequest方法處理。這個(gè)方法怎么實(shí)現(xiàn)取決于這個(gè)線(xiàn)程想做什么。

ios 多線(xiàn)程開(kāi)發(fā)(三)Run Loops


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 91日本在线观看亚洲精品 | 欧美一性一看一免费视频 | 成人性大片免费观看网站 | 日韩欧美h | 国产噜噜噜 | 嫩草影院在线免费观看 | 激情六月丁香婷婷 | 亚洲人与黑人xxxx | 中文字幕av一区二区 | 午夜影院在线看 | 国产午夜视频 | 日韩app| 亚洲人人插 | 不卡中文一二三区 | 成人毛片久久 | 国产精品1区2区3区 亚洲国产aⅴ成人精品无吗 | 欧美视频在线免费播放 | 暴操美女视频 | 亚洲日日干 | 99亚洲精品高清一二区 | 日本黄色大片免费观看 | 91视频网页版 | www.精品 | 在线视频h| 国产欧美性综合视频性刺激 | 日韩二区 | 欧美又黄又嫩大片a级 | 欧美国产日本高清不卡 | 免费国产视频 | 日本高清免费不卡毛片 | 国产日韩亚洲不卡高清在线观看 | 日本v片做爰免费视频网站 国产精品v欧美精品v日韩精品 | 波多野结衣a∨免费观看 | 欧美精品欧美精品系列 | 九九影院理论片 | 婷婷丁香综合 | 久久久久久久 | 91精品观看91久久久久久 | 色偷偷成人网免费视频男人的天堂 | 国产高清亚洲 | 成人毛片国产a |