<譯> iOS Run Loops

翻譯來(lái)源: ?RunLoops?

Run Loops

????????RunLoops是與線程緊密相關(guān)的基礎(chǔ)架構(gòu)的一部分,簡(jiǎn)稱運(yùn)行循環(huán)递览。RunLoop是一個(gè)事件處理循環(huán),用于安排工作并協(xié)調(diào)接收到的事件。RunLoop的目的是在有任務(wù)的時(shí)候線程處于繁忙狀態(tài)(thread busy),并在沒(méi)有任務(wù)的時(shí)候線程處于休眠狀態(tài)(thread sleep)芽隆。

????????RunLoop管理不是完全自動(dòng)的。我們?nèi)匀恍枰O(shè)計(jì)線程的代碼以便在適當(dāng)?shù)臅r(shí)間啟動(dòng)RunLoop并響應(yīng)傳入的事件统屈。Cocoa和Core Foundation都提供了RunLoop對(duì)象胚吁,以幫助我們配置和管理線程的RunLoop。我們不需要明確的創(chuàng)建RunLoop對(duì)象愁憔;每一個(gè)線程(包含主線程在內(nèi)的)都有一個(gè)與之關(guān)聯(lián)的RunLoop對(duì)象腕扶。但是,只有次線程需要顯式的運(yùn)行RunLoop吨掌。作為應(yīng)用程序啟動(dòng)的一部分半抱,應(yīng)用程序框架自動(dòng)設(shè)置并在主線程上運(yùn)行RunLoop。

????????下面將提供有關(guān)RunLoop的更多信息以及如何為應(yīng)用程序配置它們膜宋。有關(guān)RunLoop對(duì)象的更多信息窿侈,請(qǐng)參考?NSRunLoop Class Reference?and?CFRunLoop Reference.

一、Run Loop的解析

????????RunLoop就像它的名字一樣是一個(gè)運(yùn)行循環(huán)秋茫。在這個(gè)檢測(cè)循環(huán)內(nèi)史简,使用與之綁定的線程來(lái)運(yùn)行事件處理程序以響應(yīng)傳入的事件。我們的代碼用于實(shí)現(xiàn)RunLoop的實(shí)際循環(huán)部分的控制語(yǔ)句肛著,換句話說(shuō)圆兵,這部分代碼將驅(qū)動(dòng)RunLoop的while或者for循環(huán)。在循環(huán)中枢贿,使用RunLoop對(duì)象來(lái)“運(yùn)行”接收事件并調(diào)用已安裝處理程序的代碼處理事件殉农。

????????Run Loop接收兩種不同類型源的事件。輸入源(input sources)傳遞異步事件萨咕,通常是來(lái)自另一個(gè)線程或者來(lái)自不同的應(yīng)用程序的消息统抬。計(jì)時(shí)器源(Timer sources)提供發(fā)生在計(jì)劃時(shí)間或重復(fù)間隔的同步事件。這兩種類型的源都使用特定于應(yīng)用程序的處理程序來(lái)處理事件到達(dá)時(shí)的狀態(tài)危队。

????????圖(1-1)顯示了RunLoop和各種來(lái)源的概念結(jié)構(gòu)聪建。輸入源(input sources)將異步事件傳遞給相應(yīng)的處理程序,并導(dǎo)致runUntilDate:方法(在線程關(guān)聯(lián)的RunLoop對(duì)象上調(diào)用)退出茫陆。計(jì)時(shí)器源(Timer source)將事件傳遞到其處理程序例程金麸,但不會(huì)導(dǎo)致RunLoop退出。

圖(1-1)運(yùn)行循環(huán)及其來(lái)源的的結(jié)構(gòu)

????????除了處理輸入源(input sources)之外簿盅, Run Loop還會(huì)生成有關(guān)RunLoop行為的通知挥下。已注冊(cè)的Run Loop觀察器可以接收這些通知并使用它們對(duì)線程進(jìn)行附加處理揍魂。我們可以使用Core Foundtion 在線程上安裝Run Loop觀察器。

????????下面的部分提供了關(guān)于RunLoop的組件和其運(yùn)行模式的更多信息棚瘟。它們還描述了在處理事件期間在不同時(shí)間生成的通知现斋。

1.1Run Loop模式

????????RunLoop模式是要監(jiān)視的輸入源和計(jì)時(shí)器的集合,以及要通知的RunLoop觀察器的集合偎蘸。每次運(yùn)行RunLoop時(shí)庄蹋,都需要(顯式或隱式的)指定要運(yùn)行的特定"模式"。在運(yùn)行循環(huán)的過(guò)程中迷雪,只監(jiān)視與該模式關(guān)聯(lián)的源并允許其發(fā)送事件限书。(同樣,只有與該模式關(guān)聯(lián)的觀察器才會(huì)收到RunLoop進(jìn)程的通知章咧。)與其他模式相關(guān)的源保存到任何新事件倦西,直到隨后以適當(dāng)?shù)哪J酵ㄟ^(guò)循環(huán)為止。

????????在代碼中赁严,我們可以通過(guò)名字來(lái)識(shí)別模式扰柠。Cocoa和Core Foundation都定義了一個(gè)默認(rèn)模式和幾種常用模式,以及用于在代碼中指定這些模式的字符串疼约。我們可以通過(guò)為模式名稱指定自定義字符串定義自定義模式耻矮。雖然分配給自定義模式的名稱是任意的,但這些模式的內(nèi)容不是忆谓。因此必須確保將一個(gè)或者多個(gè)輸入源、計(jì)時(shí)器或者RunLoop觀察器添加到與我們創(chuàng)建的模式中踱承,以便它們有用倡缠。

? ? ????在特定的RunLoop中使用模式可以過(guò)濾掉不需要的源中的事件。大多數(shù)情況下茎活,我們需要在系統(tǒng)定義的“默認(rèn)”模式下運(yùn)行RunLoop昙沦。但是,模態(tài)面板可能會(huì)以“模態(tài)”模式運(yùn)行载荔。在此模式下盾饮,只有與模態(tài)面板相關(guān)的源才會(huì)將事件傳遞給線程。對(duì)于輔助線程懒熙,也可以使用自定義模式來(lái)防止低優(yōu)先級(jí)的源在時(shí)間關(guān)鍵型操作期間傳遞事件丘损。

注意:模式基于事件的源進(jìn)行區(qū)分,而不是事件的類型工扎。例如徘钥,我們不會(huì)使用模式僅匹配鼠標(biāo)點(diǎn)擊(mousedown)事件或僅匹配鍵盤(pán)事件。我們也可以使用模式監(jiān)聽(tīng)一組不同的端口肢娘,暫時(shí)暫停計(jì)時(shí)器呈础,或者更改當(dāng)前正在監(jiān)視的源代碼和RunLoop觀察器舆驶。

????????下面列出了Cocoa和Core Foundation定義的標(biāo)準(zhǔn)模式以及何時(shí)使用該模式的描述。 ? ?

模式1?

model : ? ? Default?

?Name:NSDefaultRunLoopModel(Cocoa) ? kCFRunLoopDefaultMode(Core Foundation)

Description ?: 默認(rèn)模式是用于大多數(shù)操作的模式而钞。 大多數(shù)情況下沙廉, 我們應(yīng)該使用此模式來(lái)啟動(dòng)RunLoop并配置輸入源。

模式2:

model :? Connection

?Name:?NSConnectionReplyModel(Cocoa)

Description ?: Cocoa將此模式與NSConnection對(duì)象一起使用來(lái)監(jiān)聽(tīng)響應(yīng)臼节。我們應(yīng)該很少需要自己使用這種模式撬陵。

模式3:

model : ?Modal

?Name:NSModelPanelRunLoopModels(Cocoa)

Description ?: ?Cocoa使用該模式識(shí)別用于模態(tài)面板的事件

模式4:

model : ?Event tracking

?Name: NSEventTrackingRunLoopModel(Cocoa)

Description ?: Cocoa使用該模式來(lái)限制鼠標(biāo)拖動(dòng)循環(huán)和其他類型的用戶界面跟蹤循環(huán)期間的傳入的事件。

模式5:?

model : ??Common modes

?Name:NSRunLoopCommonModes(Cocoa)kCFRunLoopCommonModes(Core Foundation)?

Description ?: 這是一組可配置的常用模式組官疲。將輸入源與此模式相關(guān)聯(lián)也會(huì)將其與組中的每一個(gè)模式相關(guān)聯(lián)袱结。對(duì)于Cocoa應(yīng)用程序,默認(rèn)情況下途凫,此集合包含默認(rèn)垢夹、模式和事件跟蹤模式。Core Foundation最初只包含默認(rèn)模式维费。我們也可以使用CFRunLoopAddCommonMode函數(shù)將自定義模式添加到集合果元。

1.2輸入源

????????輸入源以異步的方式向您的線程傳遞事件。事件的來(lái)源取決于輸入源的類型犀盟,它通常是兩類中的一類而晒。基于端口的輸入源監(jiān)視你的應(yīng)用程序的Mach端口阅畴。自定義輸入源監(jiān)視自定義事件源倡怎。就RunLoop而言,輸入源是基于端口的還是自定義的應(yīng)該沒(méi)有關(guān)系贱枣。系統(tǒng)通常實(shí)現(xiàn)兩種類型的輸入源监署,我們可以按照原樣使用它們。兩個(gè)來(lái)源之間的唯一區(qū)別是他們?nèi)绾伟l(fā)出信號(hào)纽哥∧品Γ基于端口的源由內(nèi)核自動(dòng)發(fā)送信號(hào),自定義源必須從另一個(gè)線程手動(dòng)發(fā)送信號(hào)春塌。

????????創(chuàng)建輸入源時(shí)晓避,可以將其分配給RunLoop的一個(gè)或多個(gè)模式。模式會(huì)影響在特定的時(shí)刻監(jiān)視哪些輸入源只壳。大多數(shù)情況下俏拱,在默認(rèn)模式下運(yùn)行RunLoop,但也可以指定自定義模式吼句。如果輸入源不處于當(dāng)前監(jiān)控的模式彰触,則會(huì)生成其生成的所有事件,直到RunLoop以正確的模式運(yùn)行命辖。

????????下面是各個(gè)輸入源的介紹。

1.2.1.基于端口的源

????????Cocoa和Core Foundation為使用與端口相關(guān)的對(duì)象和函數(shù)創(chuàng)建基于端口的輸入源提供了內(nèi)置的支持。例如唱凯,在Cocoa中,我們不需要直接創(chuàng)建輸入源么鹤。而是只需要?jiǎng)?chuàng)建一個(gè)端口對(duì)象并使用NSPort的方法將該端口添加到RunLoop。port對(duì)象為我們處理所需要輸入源的創(chuàng)建和配置味廊。

????????在Core Foundation中蒸甜,我們必須手動(dòng)創(chuàng)建端口及其RunLoop源。在這兩種情況下余佛,都使用與端口不透明類型(CFMachPortRef柠新,CFMessagePortRef或CFSocketRef)相關(guān)的函數(shù)來(lái)創(chuàng)建適當(dāng)?shù)膶?duì)象。

????????有關(guān)如何設(shè)置和配置自定義的端口源的示例辉巡,請(qǐng)參閱3.3配置基于端口的輸入源恨憎。

1.2.2.自定義輸入源

????????要?jiǎng)?chuàng)建自定義的輸入源,我們必須使用與Core Foundation中的 CFRunLoopSourceRef 不透明類型關(guān)聯(lián)的函數(shù)郊楣。我們可以使用多個(gè)回調(diào)函數(shù)來(lái)配置自定義的輸入源憔恳。Core Foundation在不同的點(diǎn)調(diào)用這些函數(shù)來(lái)配置源代碼,處理所有的傳入事件净蚤,并在源代碼從RunLoop中移除時(shí)刪除源代碼钥组。

????????除了在事件到達(dá)時(shí)定義自定義源的行為之外,還必須定義事件傳遞機(jī)制今瀑。這部分源代碼在單獨(dú)的線程上運(yùn)行程梦,負(fù)責(zé)為輸入源提供數(shù)據(jù),并在數(shù)據(jù)準(zhǔn)備好處理時(shí)用信號(hào)通知它橘荠。事件傳遞機(jī)制取決于我們作烟,但不必過(guò)于復(fù)雜。

????????有關(guān)如何創(chuàng)建自定義輸入源的示例砾医,請(qǐng)參閱下文定義自定義的輸入源。有關(guān)自定義輸入源的參考信息衣厘,請(qǐng)參閱CFRunLoopSource參考如蚜。

1.2.3.Cocoa執(zhí)行選擇器源

????????除了基于端口的源代碼之外,Cocoa還定義了一個(gè)自定義輸入源影暴,允許我們?cè)谌魏尉€程執(zhí)行選擇器错邦。與基于端口的源一樣,執(zhí)行選擇器請(qǐng)求在目標(biāo)線程上被序列化型宙,從而減輕了在一個(gè)線程上運(yùn)行多個(gè)方法時(shí)可能發(fā)生的許多同步問(wèn)題撬呢。與基于端口的源不同,執(zhí)行選擇器源在執(zhí)行其選擇器后從RunLoop中移除妆兑。

注意:在OS X v10.5之前魂拦,執(zhí)行選擇器源主要用于將消息發(fā)送到主線程毛仪,但在OS X v10.5及更高版本和iOS中,可以使用它們向任何線程發(fā)送消息芯勘。

????????在另一個(gè)線程上執(zhí)行選擇器時(shí)箱靴,目標(biāo)線程必須具有活動(dòng)的RunLoop。對(duì)于我們創(chuàng)建的線程荷愕,這意味著等待我們的代碼明確的啟動(dòng)RunLoop衡怀。但是,因?yàn)橹骶€程的RunLoop是在應(yīng)用程序調(diào)用應(yīng)用程序的委托的applicationDidFinishLaunching:方法時(shí)自己?jiǎn)?dòng)的安疗,因此在該方法調(diào)用之后就可以開(kāi)始在主線程上發(fā)出調(diào)用抛杨。RunLoop每次通過(guò)循環(huán)處理所有排隊(duì)的執(zhí)行選擇器調(diào)用,而不是在每次循環(huán)迭代期間處理一個(gè)荐类。

????????下面列出了NSObject上定義的方法怖现,可用于在其他線程上執(zhí)行選擇器。因?yàn)檫@些方法是在NSObject上聲明的掉冶,所以我們可以在任何有權(quán)訪問(wèn)NSObject對(duì)象的線程中使用它們真竖,包括POSIX線程。這些方法實(shí)際上不會(huì)創(chuàng)建一個(gè)新的線程來(lái)執(zhí)行選擇器厌小。

????????在其他線程上執(zhí)行選擇器

方法1.

performSelectorOnMainThread:withObject:waitUntilDone:

performSelectorOnMainThread:withObject:waitUntilDone:modes:

????????以上方法在該線程的下一個(gè)運(yùn)行循環(huán)中執(zhí)行應(yīng)用程序主線程上的指定選擇器恢共。這些方法使我們可以選擇阻止當(dāng)前線程,直到執(zhí)行選擇器璧亚。

方法2.

performSelector:onThread:withObject:waitUntilDone:

performSelector:onThread:withObject:waitUntilDone:modes:

????????以上方法在擁有NSThread對(duì)象的任何線程上執(zhí)行指定的選擇器讨韭。這些方法可以選擇是否阻止當(dāng)前線程,直到執(zhí)行選擇器癣蟋。

方法3.

performSelector:withObject:afterDelay:

performSelector:withObject:afterDelay:inModes:

????????以上方法在下一個(gè)運(yùn)行循環(huán)周期和可選的延遲周期之后透硝,在當(dāng)前線程上執(zhí)行指定的選擇器。由于它等待下一個(gè)RunLoop執(zhí)行選擇器疯搅,所以這些方法會(huì)從當(dāng)前正在執(zhí)行的代碼中提供一個(gè)微型的自動(dòng)延遲濒生。多個(gè)排隊(duì)選擇器按照他們排隊(duì)的順序依次執(zhí)行。

方法4.

cancelPreviousPerformRequestsWithTarget:

cancelPreviousPerformRequestsWithTarget:selector:object:

????????以上方法允許我們使用performSelector:withObject:afterDelay:或performSelector:withObject:afterDelay:inModes:方法取消發(fā)送到當(dāng)前線程的消息幔欧。

1.2.4:定時(shí)器源

????????定時(shí)器源在未來(lái)的預(yù)設(shè)時(shí)間將事件同步傳遞給線程罪治。定時(shí)器源是線程通知自己做某事的一種方式。例如礁蔗,搜索字段可以使用定時(shí)器在用戶的連續(xù)擊鍵之間經(jīng)過(guò)一段之間后啟動(dòng)自動(dòng)搜索觉义。使用此延遲時(shí)間使用戶有機(jī)會(huì)再開(kāi)始搜索之前盡可能多地輸入所需的搜索字符串。

????????雖然它生成基于時(shí)間的通知浴井,但計(jì)時(shí)器不是實(shí)時(shí)機(jī)制晒骇。與輸入源一樣,定時(shí)器與RunLoop的特定模式相關(guān)聯(lián)。如果一個(gè)定時(shí)器不處于當(dāng)前由RunLoop監(jiān)視的模式洪囤,那么只有在定時(shí)器的一種受支持模式下運(yùn)行RunLoop時(shí)才會(huì)觸發(fā)定時(shí)器徒坡。同樣,如果運(yùn)行循環(huán)處于處理程序歷程的中間時(shí)觸發(fā)定時(shí)器箍鼓,則定時(shí)器將等待下一次通過(guò)RunLoop來(lái)調(diào)用其處理程序例程崭参。如果RunLoop根本沒(méi)有運(yùn)行,則定時(shí)器不會(huì)啟動(dòng)款咖。

????????我們可以將定時(shí)器配置為僅生成一次或重復(fù)生成事件何暮。重復(fù)計(jì)時(shí)器會(huì)根據(jù)預(yù)定的執(zhí)行時(shí)間自動(dòng)重新安排時(shí)間,而不是實(shí)際的執(zhí)行時(shí)間铐殃。例如海洼,如果定時(shí)器計(jì)劃在特定的執(zhí)行時(shí)間以及之后每隔5秒觸發(fā)一次,則即使實(shí)際觸發(fā)時(shí)間延遲富腊,計(jì)劃的觸發(fā)時(shí)間也會(huì)始終以最初的5秒為間隔坏逢。如果開(kāi)始時(shí)間延遲太多以至于未能到達(dá)一個(gè)或者多個(gè)預(yù)定的執(zhí)行時(shí)間,則計(jì)時(shí)器在錯(cuò)過(guò)的時(shí)間段內(nèi)僅被執(zhí)行一次赘被。在錯(cuò)過(guò)的執(zhí)行時(shí)間后是整,定時(shí)器重新安排下一個(gè)預(yù)設(shè)的執(zhí)行時(shí)間。

????????有關(guān)配置定時(shí)器源的更多信息民假,請(qǐng)參閱3.2配置定時(shí)器源浮入。有關(guān)參考信息,請(qǐng)參閱NSTimer類參考CFRunLoopTimer參考羊异。

1.2.5:RunLoop觀察器

????????與發(fā)生適當(dāng)異步事件或同步事件時(shí)觸發(fā)的源相比事秀,RunLoop觀察器在RunLoop本身的執(zhí)行過(guò)程中會(huì)在特定位置觸發(fā)。我們可以使用RunLoop觀察器來(lái)準(zhǔn)備線程以及處理給定的事件野舶,或者在線程進(jìn)入休眠之前準(zhǔn)備線程易迹。我們可以將RunLoop觀察程序與RunLoop中的以下事件相關(guān)聯(lián):

1.進(jìn)入RunLoop。

2.RunLoop即將處理計(jì)時(shí)器平道。

3.RunLoop即將處理輸入源睹欲。

4.RunLoop即將進(jìn)入睡眠狀態(tài)。

5.RunLoop喚醒一屋,但在處理喚醒他的事件之前窘疮。

6.退出RunLoop。

????????我們可以使用Core Foundation將RunLoop觀察器添加到應(yīng)用程序陆淀。要?jiǎng)?chuàng)建RunLoop觀察器,可以創(chuàng)建CFRunLoopObserverRef不透明類型的實(shí)例先嬉。此類型會(huì)跟蹤自定義您的自定義回調(diào)函數(shù)以及它感興趣的活動(dòng)轧苫。

????????與定時(shí)器類似,RunLoop觀察器可以使用一次或重復(fù)使用。一次觀察者在執(zhí)行回調(diào)函數(shù)后從RunLoop中刪除含懊,而重復(fù)觀察者仍然在RunLoop中身冬。我們可以指定觀察者在創(chuàng)建時(shí)是運(yùn)行一次還是重復(fù)運(yùn)行。

????????有關(guān)如何創(chuàng)建運(yùn)行循環(huán)觀察程序的示例岔乔,請(qǐng)參考2.2配置運(yùn)行循環(huán)酥筝。有關(guān)參考信息,請(qǐng)參閱CFRunLoopObserver參考

1.2.6:事件的RunLoop執(zhí)行次序

????????每次運(yùn)行RunLoop時(shí)雏门,線程的RunLoop都會(huì)處理未決事件嘿歌,并為任何附加的觀察者生成通知。它的執(zhí)行順序非常具體茁影,如下所示:

1.通知觀察者已經(jīng)進(jìn)入RunLoop宙帝。

2.通知觀察者計(jì)時(shí)器執(zhí)行準(zhǔn)備就緒。

3.通知觀察者募闲,非基于端口的輸入源即將觸發(fā)步脓。

4.啟動(dòng)任何可以觸發(fā)的非基于端口的輸入源。

5.如果基于端口的輸入源已準(zhǔn)備好并正在等待觸發(fā)浩螺,請(qǐng)立即處理該事件靴患。跳轉(zhuǎn)到第9步。

6.通知觀察者該線程即將進(jìn)入休眠要出。

7.讓線程進(jìn)入休眠狀態(tài)鸳君,直到發(fā)生以下事件之一:

<1>一個(gè)基于端口的輸入源事件到達(dá)。

<2>計(jì)時(shí)器啟動(dòng)厨幻。

<3>RunLoop超時(shí)相嵌。

<4>RunLoop被明確地喚醒。

8.通知觀察者線程剛剛喚醒况脆。

9饭宾。處理未決事件

<1>如果用戶定義的定時(shí)器啟動(dòng),則處理定時(shí)器事件并重新啟動(dòng)循環(huán)格了。跳轉(zhuǎn)到第2步看铆。

<2>如果輸入源被觸發(fā),則交付事件盛末。

<3>如果RunLoop顯式喚醒但尚未超時(shí)弹惦,重新啟動(dòng)RunLoop。跳轉(zhuǎn)到第2步悄但。

10.通知觀察者RunLoop已退出棠隐。

????????因?yàn)槎〞r(shí)器和輸入源的觀察者通知是在這些事件實(shí)際發(fā)生前交付的,所以通知時(shí)間和實(shí)際事件時(shí)間之間可能存在差距檐嚣。如果這些事件之間的時(shí)間很關(guān)鍵助泽,則可以使用休眠和從休眠中醒來(lái)的通知來(lái)完成關(guān)聯(lián)實(shí)際事件之間的時(shí)間。

????????由于定時(shí)器和其他周期性事件是在運(yùn)行循環(huán)時(shí)交付的,因此繞過(guò)該RunLoop會(huì)中斷這些事件的交付嗡贺。無(wú)論何時(shí)通過(guò)輸入一個(gè)循環(huán)并重復(fù)地從應(yīng)用程序請(qǐng)求事件來(lái)實(shí)現(xiàn)鼠標(biāo)跟蹤例程隐解,都會(huì)發(fā)生此行為的典型示例。由于我們的代碼直接抓取事件诫睬,而不是讓?xiě)?yīng)用程序正常調(diào)度這些事件煞茫,因此在鼠標(biāo)跟蹤例程退出并將控制返回給應(yīng)用程序之前,激活的定時(shí)器將無(wú)法觸發(fā)摄凡。

????????RunLoop可以使用RunLoop對(duì)象顯式喚醒续徽。其他事件也可能導(dǎo)致RunLoop被喚醒。例如架谎,添加另一個(gè)基于非端口的輸入源會(huì)喚醒RunLoop炸宵,以便于可以立即處理輸入源,而不是等待其他事件發(fā)生谷扣。

二:使用RunLoop對(duì)象

????????RunLoop對(duì)象提供了將輸入源土全,定時(shí)器和運(yùn)行循環(huán)觀察器添加到RunLoop并運(yùn)行它的主要接口。每個(gè)線程都有一個(gè)與之關(guān)聯(lián)的RunLoop對(duì)象会涎。在Cocoa中裹匙,這個(gè)對(duì)象是NSRunLoop類的一個(gè)實(shí)例。在底層的應(yīng)用程序中末秃,它是一個(gè)指向CFRunLoopRef類型的指針概页。

2.1.獲取RunLoop對(duì)象

????????可以使用如下方式來(lái)獲取當(dāng)前線程的RunLoop:

<1>在Cocoa應(yīng)用程序中,使用NSRunLoop的currentRunLoop類方法來(lái)檢索NSRunLoop對(duì)象练慕。

<2>使用CFRunLoopGetCurrent函數(shù)惰匙。

? ? ????雖然它們不是免費(fèi)的橋接類型(toll-free bridged types),但在需要時(shí),我們可以從NSRunLoop對(duì)象獲取CFRunLoopRef不透明類型铃将。NSRunLoop類定義了一個(gè)getCFRunLoop方法项鬼,該方法返回可以傳遞給Core Foundation例程的CFRunLoopRef類型。由于兩個(gè)對(duì)象都引用同一個(gè)RunLoop劲阎,因此可以根據(jù)需要將調(diào)用混合到NSRunLoop對(duì)象和CFRunLoopRef不透明類型绘盟。

2.2.配置RunLoop

????????在子線程上運(yùn)行RunLoop之前,我們必須至少添加一個(gè)輸入源或計(jì)時(shí)器悯仙。如果RunLoop沒(méi)有任何監(jiān)控的來(lái)源龄毡,當(dāng)嘗試運(yùn)行RunLoop時(shí),它會(huì)立即退出锡垄。有關(guān)如何將源添加到RunLoop的示例沦零,請(qǐng)參考第三節(jié)配置RunLoop源

????????除了安裝源代碼之外货岭,我們還可以安裝RunLoop觀察器并使用它們來(lái)檢測(cè)RunLoop的不同執(zhí)行階段路操。要安裝RunLoop觀察器序攘,需要?jiǎng)?chuàng)建一個(gè)CFRunLoopObserverRef 不透明類型,并使用CFRunLoopAddObserver函數(shù)將其添加到RunLoop中寻拂。RunLoop觀察者必須使用Core Foundation創(chuàng)建,即使對(duì)于Cocoa應(yīng)用程序也是如此丈牢。

? ? ? ? 圖(2-1-1)顯示了將RunLoop觀察器連接到其RunLoop的線程的主例程祭钉。該示例的目的是向您展示如何創(chuàng)建RunLoop觀察器,因此代碼只需要設(shè)置一個(gè)RunLoop觀察器即可監(jiān)視所有RunLoop活動(dòng)己沛』藕耍基本處理程序例程只是在處理計(jì)時(shí)器請(qǐng)求時(shí)記錄RunLoop活動(dòng)。

圖(2-1-1)添加關(guān)聯(lián)RunLoop的觀察器

????????如果我們想配置一個(gè)一直運(yùn)行的RunLoop申尼,最好添加至少一個(gè)輸入源接收消息垮卓。盡管我們可以在進(jìn)入RunLoop是關(guān)聯(lián)一個(gè)定時(shí)器,一旦定時(shí)器開(kāi)始师幕,它通常會(huì)面臨失效粟按,一旦定時(shí)器失效RunLoop便會(huì)退出。附加重復(fù)計(jì)時(shí)器可以使RunLoop長(zhǎng)時(shí)間運(yùn)行霹粥,但是會(huì)定時(shí)觸發(fā)計(jì)時(shí)器喚醒線程灭将,這實(shí)際上是另一種輪詢方式。相反后控,輸入源會(huì)等待事件發(fā)生庙曙,讓線程一直處于休眠狀態(tài),知道它(事件)發(fā)生浩淘。

2.3.啟動(dòng)RunLoop

????????啟動(dòng)RunLoop僅適用于應(yīng)用程序中的子線程捌朴。RunLoop必須至少有一個(gè)輸入源和計(jì)時(shí)器才能進(jìn)行監(jiān)視。如果沒(méi)有张抄,則RunLoop立即退出砂蔽。

????????有以下幾種開(kāi)啟RunLoop的方式,包括:

<1>無(wú)條件的

<2>具有設(shè)定的時(shí)間限制

<3>在特定模式下

????????無(wú)條件的進(jìn)入RunLoop是最簡(jiǎn)單的選擇欣鳖,但他也是最不可取的察皇。無(wú)條件的運(yùn)行你的RunLoop會(huì)把你的線程放到一個(gè)永久循環(huán)中,這使你很少控制RunLoop本身泽台。我們可以添加和刪除輸入源和定時(shí)器什荣,但停止RunLoop的唯一方法是殺死它,同樣在自定義模式下怀酷,我們也無(wú)法運(yùn)行RunLoop稻爬。

????????不使用無(wú)條件地運(yùn)行RunLoop,最好使用超時(shí)值運(yùn)行RunLoop蜕依。當(dāng)我們?cè)O(shè)置超時(shí)值時(shí)桅锄,RunLoop會(huì)一直運(yùn)行琉雳,知道事件到達(dá)或分配的時(shí)間到期。如果事件到達(dá)友瘤,則將該事件分派給處理程序進(jìn)行處理翠肘,然后RunLoop退出。我們使用代碼重新啟動(dòng)RunLoop來(lái)處理下一個(gè)事件辫秧。如果分配的時(shí)間到期束倍,我們可以簡(jiǎn)單地重新啟動(dòng)RunLoop或使用時(shí)間來(lái)完成所需的任務(wù)管理。

????????除了超時(shí)值之外盟戏,我們也可以使用特定模式運(yùn)行RunLoop绪妹。模式和超時(shí)值不是互斥的,并且在啟動(dòng)RunLoop時(shí)都可以使用柿究。模式限制將事件傳遞到RunLoop的源的類型邮旷,并在RunLoop模式中有更詳細(xì)的描述。

? ? ? ? 圖(2-1-2)顯示了一個(gè)線程的主要入口示例的框架版本蝇摸。這個(gè)例子的關(guān)鍵部分顯示了RunLoop的基本結(jié)構(gòu)婶肩。本質(zhì)上,我們將輸入源和定時(shí)器添加到RunLoop中貌夕,然后重復(fù)調(diào)用其中一個(gè)示例來(lái)啟動(dòng)RunLoop狡孔。每次RunLoop示例返回時(shí),都會(huì)檢查是否有任何可能導(dǎo)致退出線程的情況蜂嗽。該示例使用Core FoundationRunLoop示例苗膝,以便它可以檢查返回結(jié)果并確定RunLoop退出的原因。如果使用Cocoa并且不需要檢查返回值植旧,我們也可以使用NSRunLoop類的方法以類似的方式運(yùn)行RunLoop辱揭。(有關(guān)調(diào)用NSRunLoop類的方法運(yùn)行循環(huán)的示例,請(qǐng)參考圖(3-3-3)病附。)

圖(2-1-2)運(yùn)行一個(gè)RunLoop

????????它可以遞歸運(yùn)行一個(gè)RunLoop问窃。換句話說(shuō),我們可以調(diào)用CFRunLoopRun,CFRunLoopRunInMode或任何NSRunLoop方法來(lái)從輸入源或定時(shí)器的處理程序中啟動(dòng)RunLoop。當(dāng)這樣做時(shí)铸题,可以使用任何mode運(yùn)行嵌套的RunLoop迄汛,包括外部嵌套使用的模式硕淑。

2.4.退出RunLoop

????????在處理事件之前,有兩種方式可以使RunLoop退出:

<1>以超時(shí)值配置RunLoop運(yùn)行。

<2>告訴RunLoop停止。

????????如果可以管理它尉姨,使用超時(shí)值肯定是首選。指定超時(shí)值可讓RunLoop完成所有正常處理吗冤,包括在退出之前將通知發(fā)送到RunLoop觀察器又厉。

????????使用CFRunLoopStop函數(shù)顯式停止RunLoop會(huì)產(chǎn)生類似于超時(shí)的結(jié)果九府。RunLoop發(fā)出任何剩余的RunLoop通知,然后退出覆致。不同的是侄旬,我們可以在無(wú)條件開(kāi)啟的RunLoop中使用此技術(shù)。

????????盡管刪除RunLoop的輸入源和定時(shí)器也可能導(dǎo)致RunLoop退出煌妈,但這不是停止RunLoop的可靠方法勾怒。一些系統(tǒng)例程將輸入源添加到RunLoop以處理所需的事件。但是我們的代碼可能沒(méi)有意識(shí)到這些輸入源声旺,所以它將無(wú)法刪除他們,這將阻止RunLoop退出段只。

2.5.線程安全和RunLoop對(duì)象

????????線程安全取決于我們使用哪個(gè)API來(lái)操作運(yùn)行循環(huán)腮猖。Core Foundation中的函數(shù)通常是線程安全的,可以從任何線程中調(diào)用赞枕。但是澈缺,如果要執(zhí)行更改RunLoop配置的操作,則盡可能從擁有RunLoop的線程執(zhí)行此操作仍是一種好的做法炕婶。

????????Cocoa 中的NSRunLoop類并不想其核心機(jī)處對(duì)象那樣天生就是線程安全的姐赡。如果使用NSRunLoop類來(lái)修改RunLoop,則應(yīng)該只從擁有該RunLoop的同一個(gè)線程來(lái)完成柠掂。將輸入源或定時(shí)器源添加到屬于不同線程的RunLoop中可能會(huì)導(dǎo)致代碼崩潰或以意外的方式運(yùn)行项滑。

三:配置RunLoop源

以下部分顯示了如何在Cocoa和Core Foundation中設(shè)置不同類型的輸入源示例。

3.1.定義自定義輸入源

????????創(chuàng)建自定義輸入源包括定義以下內(nèi)容:

<1>希望輸入源處理的信息涯贞。

<2>調(diào)度程序讓有興趣的客戶知道如何聯(lián)系您的輸入源枪狂。

<3>處理程序例程,用于執(zhí)行任何客戶端發(fā)送的請(qǐng)求宋渔。

<4>取消例程以使輸入源無(wú)效州疾。

????????由于我們創(chuàng)建了一個(gè)自定義的輸入源來(lái)處理自定義的信息,因此實(shí)際配置是設(shè)計(jì)靈活的皇拣。調(diào)度程序严蓖,處理程序和取消例程幾乎總是需要用于自定義輸入源的關(guān)鍵例程。然而氧急,大部分輸入源的行為的其余部分都發(fā)生在這些處理程序之外颗胡。例如,我們需要定義將數(shù)據(jù)傳遞到輸入源并將輸入源的存在傳遞給其他線程的機(jī)制吩坝。

????????下圖(3-1)顯示了自定義輸入源的示例配置杭措。在本例中,應(yīng)用程序的主線程保持對(duì)輸入源钾恢,該輸入源的自定義命令緩沖區(qū)以及安裝輸入源的RunLoop的引用手素。當(dāng)主線程有一個(gè)任務(wù)想要切換到工作線程時(shí)鸳址,它將命令發(fā)送到命令緩沖區(qū)以及工作線程啟動(dòng)任務(wù)所需的任何信息。(因?yàn)楣ぷ骶€程的主線程和輸入源都可以訪問(wèn)命令緩沖區(qū)泉懦,所以訪問(wèn)必須同步稿黍。)一旦命令發(fā)布,主線程就會(huì)發(fā)信號(hào)通知輸入源并喚醒工作線程的RunLoop崩哩。在收到喚醒命令后巡球,RunLoop會(huì)調(diào)用輸入源的處理程序,該輸入源處理命令緩沖區(qū)中的命令邓嘹。

圖(3-1)操作自定義的輸入源

下面各節(jié)將解釋上圖中自定義輸入源的實(shí)現(xiàn)酣栈,并顯示需要實(shí)現(xiàn)的關(guān)鍵代碼。

3.1.1.定義輸入源

????????定義自定義輸入源需要使用Core Foundation例程來(lái)配置RunLoop源并將其附加到RunLoop汹押。雖然基本的處理程序是基于C的函數(shù)矿筝,但這并不妨礙我們?yōu)檫@些函數(shù)編寫(xiě)包裝,并使用Objective-C或C++來(lái)實(shí)現(xiàn)代碼的主題棚贾。

????????圖3-1中介紹的輸入源使用Objective-C對(duì)象來(lái)管理命令緩沖區(qū)并與RunLoop進(jìn)行協(xié)調(diào)窖维。圖(3-1-1)顯示了這個(gè)對(duì)象的定義。RunLoopSpurce對(duì)象管理命令緩沖區(qū)并使用該緩沖區(qū)接收來(lái)自其他線程的消息妙痹。此圖還顯示RunLoopContext對(duì)象的自定義铸史,該對(duì)象實(shí)際上只是一個(gè)容器,用于將RunLoopSpurce對(duì)象和RunLoop引用傳遞給應(yīng)用程序的主線程怯伊。

圖(3-1-1)定義自定義的輸入源

????????雖然Objective-C代碼管理輸入源的自定義數(shù)據(jù)琳轿,但將輸入源附加到RunLoop中需要使用基于C的回調(diào)函數(shù)。當(dāng)我們將RunLoop源實(shí)際連接到RunLoop時(shí)耿芹,將調(diào)用其中的第一個(gè)函數(shù)如圖(3-1-2)所示利赋。由于此輸入源只有一個(gè)客戶端(主線程),因此它使用調(diào)度程序函數(shù)發(fā)送消息以在該線程上向應(yīng)用程序委托注冊(cè)自己猩系。當(dāng)委托人想要與輸入源通信時(shí)媚送,它使用RunLoopContext對(duì)象中的信息來(lái)執(zhí)行此操作。

圖(3-1-2)運(yùn)行RunLoop源

????????最重要的回調(diào)例程之一是用于在輸入源發(fā)送信號(hào)時(shí)處理自定義數(shù)據(jù)的回調(diào)例程寇甸。圖(3-1-3)顯示了RunLoopSource對(duì)象關(guān)聯(lián)的執(zhí)行回調(diào)示例塘偎。該函數(shù)只是將請(qǐng)求執(zhí)行的請(qǐng)求轉(zhuǎn)發(fā)給SourceFired方法,然后該方法處理命令緩沖區(qū)中存在的任何命令拿霉。

圖(3-1-3)在輸入源中執(zhí)行任務(wù)

????????如果使用CFRunLoopSourceInvalidate函數(shù)將輸入源從其RunLoop中移除吟秩,系統(tǒng)將調(diào)用輸入源取得取消例程。我們可以使用此例程來(lái)通知客戶端輸入源不再有效绽淘,并且應(yīng)該刪除對(duì)它的引用涵防。圖(3-1-4)顯示了用RunLoopSpurce對(duì)象注冊(cè)的取消回調(diào)例程。該函數(shù)將另一個(gè)RunLoopContext對(duì)象發(fā)送給應(yīng)用程序委托沪铭,但是這次要求委托移除對(duì)RunLoop源的引用壮池。

圖(3-1-4)使輸入源無(wú)效

注意:應(yīng)用程序委托的registerSource:和removeSource:方法的代碼顯示在3.1.3節(jié)與輸入源的客戶端協(xié)調(diào)中偏瓤。

3.1.2在RunLoop中安裝輸入源

????????圖3-2-1顯示了RunLoopSource類的init和addToCurrentRunLoop方法。init方法創(chuàng)建實(shí)際連接到RunLoop的CFRunLoopSourceRef不透明類型椰憋。它將RunLoopSource對(duì)象本身作為上下文信息傳遞厅克,以便回調(diào)例程具有指向該對(duì)象的指針。在工作線程調(diào)用addToCurrentRunLoop方法之前橙依,不會(huì)發(fā)生輸入源的安裝证舟,此時(shí)將調(diào)用RunLoopSourceScheduleRoutine回調(diào)函數(shù)。一旦輸入源被添加到RunLoop中窗骑,線程就可以運(yùn)行它的RunLoop來(lái)等待它女责。

圖(3-1-5)安裝RunLoop源

3.1.3與輸入源的客戶端協(xié)調(diào)

????????為了使輸入源有用,我們需要操作它并從另一個(gè)線程發(fā)出信號(hào)创译。輸入源的全部要點(diǎn)是將其關(guān)聯(lián)的線程休眠直到有事情要做抵知。這個(gè)事實(shí)需要應(yīng)用程序中的其他線程知道輸入源并且有一個(gè)與之通信的方法。

????????將輸入源首次安裝在RunLoop中時(shí)昔榴,向客戶端通知輸入源的一種方法是發(fā)送注冊(cè)請(qǐng)求。我們可以根據(jù)輸入源注冊(cè)盡可能多的客戶端碘橘,或者可以將其注冊(cè)到某個(gè)中央機(jī)構(gòu)互订,然后將輸入源發(fā)布給感興趣的客戶端。圖(3-1-6)顯示了由應(yīng)用程序委托定義并在調(diào)用RunLoopSource對(duì)象的調(diào)度程序函數(shù)時(shí)調(diào)用注冊(cè)方法痘拆。該方法接收由RunLoopSource對(duì)象提供的RunLoopContext對(duì)象仰禽,并將其添加到其源列表中。此列表還顯示用于在從RunLoop中刪除輸入源時(shí)注銷(xiāo)輸入源的例程纺蛆。

圖(3-1-6)使用應(yīng)用程序代理注冊(cè)和刪除輸入源

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

????????將數(shù)據(jù)傳遞到輸入源后吐葵,客戶端必須發(fā)出信號(hào)并喚醒其RunLoop。信號(hào)源讓RunLoop知道源已準(zhǔn)備好處理桥氏。而且因?yàn)榫€程在信號(hào)發(fā)生時(shí)可能會(huì)休眠温峭,因此應(yīng)該始終明確的喚醒RunLoop。如果不這樣做字支,可能會(huì)導(dǎo)致處理輸入源的延遲凤藏。

????????圖(3-2-3)顯示了RunLoopSource對(duì)象的fireCommandsOnRunLoop方法。當(dāng)客戶端準(zhǔn)備好處理添加緩沖區(qū)的命令時(shí)堕伪,客戶端會(huì)調(diào)用此方法揖庄。

圖(3-2-3)喚醒RunLoop

注意:我們不應(yīng)該嘗試通過(guò)發(fā)送自定義輸入源來(lái)處理SIGHUP或其他類型的過(guò)程級(jí)信號(hào)。Core Foundation喚醒RunLoop的方法不是信號(hào)安全的欠雌,不應(yīng)該在應(yīng)用程序的信號(hào)處理程序中使用蹄梢。

3.2.配置定時(shí)器源

????????要?jiǎng)?chuàng)建一個(gè)計(jì)時(shí)器源,首先創(chuàng)建一個(gè)計(jì)時(shí)器對(duì)象并將其安排在RunLoop中富俄。在Cocoa中禁炒,使用NSTimer類來(lái)創(chuàng)建新的計(jì)時(shí)器對(duì)象而咆,在Core Foundation中使用CFRunLoopTimerRef不透明類型。在內(nèi)部齐苛,NSTimer類只是Core Foundation的擴(kuò)展翘盖,它提供了一些便利功能,如使用相同的方法創(chuàng)建和安排定時(shí)器的能力凹蜂。

在Cocoa中馍驯,可以使用以下任一類方法創(chuàng)建和安排計(jì)時(shí)器:

<1>scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:

<2>scheduledTimerWithTimeInterval:invocation:repeats:

????????這些方法創(chuàng)建計(jì)時(shí)器并將其添加到當(dāng)前線程的默認(rèn)模式下的RunLoop(NSDefaultRunLoopMode)。如果想創(chuàng)建一個(gè)NSTimer對(duì)象玛痊,然后使用NSRunLoop的addTimer:forMode:方法將它添加到RunLoop中汰瘫,也可以手動(dòng)安排定時(shí)器。這兩種技術(shù)基本上都是相同的擂煞,但是對(duì)定時(shí)器配置級(jí)別的控制是不同的混弥。如創(chuàng)建計(jì)時(shí)器并將其手動(dòng)添加到RunLoop中則可以使用除默認(rèn)模式之外的模式執(zhí)行此操作。圖(3-2-1)顯示了如何使用這兩種技術(shù)創(chuàng)建定時(shí)器对省。第一個(gè)定時(shí)器的初始延遲時(shí)間為1秒蝗拿,但之后每0.1秒定時(shí)觸發(fā)一次。第二個(gè)定時(shí)器在初始0.2秒延遲后開(kāi)始執(zhí)行蒿涎,然后每0.2秒執(zhí)行一次哀托。

圖(3-2-1)使用NSTimer創(chuàng)建安排計(jì)時(shí)器

????????圖(3-2-2)顯示了Core Foundation配置計(jì)時(shí)器所需的代碼。雖然此示例沒(méi)有在上下文中傳遞任何用戶定義的信息劳秋,但可以使用此結(jié)構(gòu)來(lái)傳遞計(jì)時(shí)器所需的自定義數(shù)據(jù)仓手。

圖(3-2-2)使用CoreFoundation創(chuàng)建和安排計(jì)時(shí)器

3.3.配置基于端口的輸入源

????????Cocoa和Core Foundation都提供了用于線程間或進(jìn)程間通信的基于端口的對(duì)象。下面介紹如何使用幾種不同類型的端口設(shè)置端口通信玻淑。

3.3.1配置一個(gè)NSMachPort對(duì)象

????????要與NSMachPort對(duì)象建立本地連接嗽冒,需要?jiǎng)?chuàng)建端口對(duì)象并將其添加到主線程的RunLoop中。啟動(dòng)輔助線程時(shí)补履,將相同的對(duì)象傳遞給線程的入口函數(shù)添坊。子線程可以使用相同的對(duì)象將消息發(fā)送回主線程。

<1>主線程的實(shí)現(xiàn)代碼

????????圖(3-3-1)顯示了啟動(dòng)輔助工作線程的主線程代碼箫锤。因?yàn)镃ocoa框架執(zhí)行許多配置端口和RunLoop的干預(yù)步驟帅腌,所以lanuchThread方法明顯短于其Core Foundation等價(jià)物;然而兩者的行為幾乎完全相同麻汰。一個(gè)區(qū)別是該方法不是將本地端口的名稱發(fā)送給工作線程速客,而是直接發(fā)送給NSPort對(duì)象。

圖(3-3-1)主線程啟動(dòng)方法

????????為了在線程之間建立一個(gè)雙向通信通道五鲫,我們可能希望工作線程在check-in消息中發(fā)送本地端口到主線程溺职。收到check-in消息后,主線程會(huì)知道啟動(dòng)第二線程時(shí)一切順利,并且還提供了一種將更多消息發(fā)送到該線程的方法。

????????圖(3-3-2)顯示了主線程的handlePortMessage:方法浪耘。當(dāng)數(shù)據(jù)到達(dá)線程自己的本地端口時(shí)調(diào)用此方法乱灵。當(dāng)check-in消息到達(dá)時(shí),該方法直接從端口消息中檢索輔助線程的端口并將其保存以供以后使用七冲。

圖(3-3-2)處理Mach端口消息

注意:如果您創(chuàng)建的是iOS項(xiàng)目此代碼會(huì)報(bào)錯(cuò)痛倚,因?yàn)镹SPortMessage目前只支持macOS 10.0+之后的系統(tǒng)。

<2>輔助工作線程的實(shí)現(xiàn)代碼

????????對(duì)于輔助工作線程澜躺,必須使用指定的端口配置線程并將信息傳回主線程蝉稳。

????????圖(3-3-3)顯示了設(shè)置工作線程的代碼。為線程創(chuàng)建一個(gè)自動(dòng)釋放池掘鄙,該方法創(chuàng)建一個(gè)工作對(duì)象來(lái)驅(qū)動(dòng)線程執(zhí)行耘戚。工作對(duì)象的sendCheckinMessage:方法(圖3-3-4)為工作線程創(chuàng)建一個(gè)本地端口,并將一個(gè)check-in消息發(fā)回主線程操漠。

圖(3-3-3)使用Mach端口啟動(dòng)工作線程

????????使用NSMachPort時(shí)收津,本地和遠(yuǎn)程線程可以使用相同的端口對(duì)象進(jìn)行線程之間的單向通信。換句話說(shuō)浊伙,由一個(gè)線程創(chuàng)建的本地端口對(duì)象成為另一個(gè)線程的遠(yuǎn)程端口對(duì)象撞秋。

????????圖(3-3-4)顯示了輔助線程的check-in示例。此方法為將來(lái)的通信設(shè)置了自己的本地端口嚣鄙,然后將check-in消息發(fā)送回主線程吻贿。該方法使用LanuchThreadWithPort:方法中收到的端口對(duì)象作為消息的目標(biāo)。

圖(3-3-4)使用Mach端口發(fā)送check-in消息

3.3.2配置一個(gè)NSMessagePort對(duì)象

????????要與NSMessagePort對(duì)象建立本地連接拗慨,我們不能簡(jiǎn)單地在線程之間傳遞端口對(duì)象廓八。遠(yuǎn)程消息端口必須按名稱獲取奉芦。在Cocoa中實(shí)現(xiàn)這一點(diǎn)需要注冊(cè)一個(gè)特定名稱的本地端口赵抢,然后將該名稱傳遞給遠(yuǎn)程線程,以便它可以獲取適當(dāng)?shù)亩丝趯?duì)象進(jìn)行通信声功。圖(3-3-5)顯示了使用消息端口的情況下端口的創(chuàng)建和注冊(cè)過(guò)程烦却。

圖(3-3-5)注冊(cè)消息端口

3.3.3在Core Foundation中配置基于端口的輸入源

????????這里介紹如何使用Core Foundation在應(yīng)用程序的主線程和工作線程之間建立雙向通信通道。

????????圖(3-3-6)顯示了應(yīng)用程序主線程調(diào)用的啟動(dòng)工作線程的代碼先巴。代碼首先設(shè)置一個(gè)CFMessagePortRef不透明類型來(lái)偵聽(tīng)來(lái)自工作線程的消息其爵。工作線程需要端口的名稱來(lái)建立連接,以便將字符串值傳遞給工作線程入口點(diǎn)函數(shù)伸蚯。端口名稱在當(dāng)前用戶上下文中通常應(yīng)該是唯一的摩渺;否則,可能會(huì)遇到?jīng)_突剂邮。

圖(3-3-6)
圖(3-3-6)為新線程附加一個(gè)Core Foundation消息端口


? ? ? ? 在安裝了端口并啟動(dòng)了線程的情況下摇幻,主線程可以在等待線程check-in時(shí)繼續(xù)執(zhí)行常規(guī)執(zhí)行。當(dāng)check-in消息到達(dá)時(shí),它將被分派到主線程的MainThreadResponseHandler函數(shù)中绰姻,如圖(3-3-7)枉侧。此函數(shù)提取工作線程的端口名稱并為未來(lái)的通信創(chuàng)建管道。

圖(3-3-7)
圖(3-3-7)接收check-in消息

? ? ? ? 在配置主線程后狂芋,剩余的唯一東西是新創(chuàng)建的工作線程創(chuàng)建自己的端口并進(jìn)行check-in榨馁。圖(3-3-8)顯示了工作線程的入口點(diǎn)函數(shù)。該函數(shù)提取主線程的端口名稱并使用它來(lái)創(chuàng)建遠(yuǎn)程連接回主線程帜矾。然后該函數(shù)為自己創(chuàng)建一個(gè)本地端口翼虫,在該線程的RunLoop中安裝端口,并向包含本地端口名稱的主線程發(fā)送一個(gè)check-in消息黍特。

圖(3-3-8)
圖(3-3-8)
圖(3-3-8)設(shè)置線程結(jié)構(gòu)

? ? ? ? 一旦它進(jìn)入RunLoop蛙讥,發(fā)送到線程端口的所有未來(lái)事件都將有ProcessClientRequest函數(shù)處理。該函數(shù)的實(shí)現(xiàn)取決于線程所執(zhí)行的工作類型灭衷,在此不顯示次慢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市翔曲,隨后出現(xiàn)的幾起案子迫像,更是在濱河造成了極大的恐慌,老刑警劉巖瞳遍,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闻妓,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡掠械,警方通過(guò)查閱死者的電腦和手機(jī)由缆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)猾蒂,“玉大人均唉,你說(shuō)我怎么就攤上這事《遣ぃ” “怎么了舔箭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蚊逢。 經(jīng)常有香客問(wèn)我层扶,道長(zhǎng),這世上最難降的妖魔是什么烙荷? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任镜会,我火速辦了婚禮,結(jié)果婚禮上终抽,老公的妹妹穿的比我還像新娘戳表。我一直安慰自己焰薄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布扒袖。 她就那樣靜靜地躺著塞茅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪季率。 梳的紋絲不亂的頭發(fā)上野瘦,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音飒泻,去河邊找鬼鞭光。 笑死,一個(gè)胖子當(dāng)著我的面吹牛泞遗,可吹牛的內(nèi)容都是我干的惰许。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼史辙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼汹买!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起聊倔,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤晦毙,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后耙蔑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體见妒,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年甸陌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了须揣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡钱豁,死狀恐怖耻卡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寥院,我是刑警寧澤劲赠,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布涛目,位于F島的核電站秸谢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏霹肝。R本人自食惡果不足惜估蹄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沫换。 院中可真熱鬧臭蚁,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至系枪,卻和暖如春雀哨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背私爷。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工雾棺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人衬浑。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓捌浩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親工秩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尸饺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容