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退出
?
另外,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í)固定的:
- 通知監(jiān)聽(tīng)者run loop進(jìn)入了
- 通知監(jiān)聽(tīng)者任何準(zhǔn)備好的定時(shí)器將要觸發(fā)
- 通知監(jiān)聽(tīng)者任何非基于port的輸入源將要觸發(fā)
- 觸發(fā)任何非基于port的事件
- 如果有任何基于port事件將要觸發(fā),處理事件,然后到第9步
- 通知監(jiān)聽(tīng)者線(xiàn)程將要掛起
-
把線(xiàn)程掛起直到下面的事件之一觸發(fā)
- 一個(gè)基于port的消息觸發(fā)
- 定義定時(shí)器觸發(fā)
- run loop設(shè)置的超時(shí)時(shí)間到了
- run loop被明確的喚醒
- 通知監(jiān)聽(tīng)者線(xiàn)程被喚醒
-
處理需要處理的事件
- 如果一個(gè)用戶(hù)定義的定時(shí)器觸發(fā),執(zhí)行定時(shí)器然后重啟消息循環(huán)。跳轉(zhuǎn)到第2步
- 如果一個(gè)事件源觸發(fā),分發(fā)事件
- 如果run loop被明確的喚醒但是還沒(méi)有超時(shí),重啟消息循環(huán)。跳轉(zhuǎn)到第2步
- 通知所有的監(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ū)中的命令。
?
定義輸入源
定義輸入源需要使用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)程想做什么。
更多文章、技術(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ì)您有幫助就好】元

