我的博客鏈接:http://superyang.gitcafe.io/blog/2016/01/18/runloop-5/
使用 Run Loop 對(duì)象
一個(gè) run loop 對(duì)象提供了一些主要接口用于向你的 run loop 中添加 input source 恩够,timers荐吵, 和run loop observer酷愧,并且運(yùn)行它跌捆。每一條線(xiàn)程有且只有一個(gè)run loop 與他相關(guān)聯(lián)葵擎。在 Cocoa 中,這個(gè)對(duì)象是 NSRunLoop 類(lèi)的一個(gè)實(shí)例狸捕。在底層的應(yīng)用中秉犹,它是指向 CFRunLoopRef 這種不透明類(lèi)型的一個(gè)指針。
獲取 Run Loop 對(duì)象
你需要使用以下其中之一來(lái)獲取當(dāng)前線(xiàn)程的 Run Loop :
- 在 Cocoa 中晚树,使用 NSRunLoop 的類(lèi)方法 currentRunLoop 去拿到一個(gè)
NSRunLoop
對(duì)象姻采。 - 使用 CFRunLoopGetCurrent 函數(shù)。
盡管這兩種方法不是 toll-free bridged type(在Foundation 和 Core Foundation 中擁有等價(jià)替換接口的能力的類(lèi)型)的類(lèi)型,但是如果你需要可以從 NSRunLoop
對(duì)象里拿到 CFRunLoopRef 這種不透明類(lèi)型
(蘋(píng)果封裝在內(nèi)部的C語(yǔ)言類(lèi)型)爵憎。NSRunLoop
類(lèi)定義了 getCFRunLoop
方法用來(lái)返回一個(gè)可以傳入到 Core Foundation 代碼中的 CFRunLoopRef
類(lèi)型的C語(yǔ)言指針對(duì)象(結(jié)構(gòu)體指針)慨亲。這兩種對(duì)象都可以來(lái)自于同一個(gè) run loop,你可以根據(jù)你的需要來(lái)選擇具體使用 NSRunLoop
和 CFRunLoopRef
這兩種對(duì)象的哪一種宝鼓。
配置 Run Loop
在你運(yùn)行一個(gè)子線(xiàn)程的 run loop 之前刑棵,你必須向其添加至少一個(gè) input source 或者 timer。如果 run loop 沒(méi)有任何需要監(jiān)視的 source愚铡, 它將會(huì)在你嘗試運(yùn)行它的時(shí)候立即退出蛉签。請(qǐng)參考配置RunLoop Sounce(本文接下來(lái)的章節(jié)將有介紹)。
除了安裝 source沥寥,你還可以 run loop observer 并且使用他們檢測(cè) runloop的處于不同執(zhí)行階段碍舍。為了安裝 run loop observer ,你需要?jiǎng)?chuàng)建一個(gè) CFRunLoopObserverRef 不透明類(lèi)型的指針并使用 CFRunLoopAddObserver 函數(shù)將 Observer 添加到你的 run loop 中去邑雅,Run Loop Observer 必須使用 Core Foundation 框架接口創(chuàng)建片橡,在 Cocoa 應(yīng)用中也一樣。
表 3-1 展示了在線(xiàn)程 runloop 中蒂阱,添加 run loop Observer 的主要代碼流程锻全。本例的目的旨在告訴你如何創(chuàng)建一個(gè) run loop Observer, 所以代碼只是簡(jiǎn)單設(shè)置了一個(gè)run loop Observer 用來(lái)監(jiān)視 run loop 的所有活動(dòng) 录煤■幔基本的處理代碼(沒(méi)有展示)僅僅是日志輸出 run loop 的各項(xiàng)活動(dòng)行為 作為 timer 的事件回調(diào)。
表3-1 創(chuàng)建 runloop Observer
- (void)threadMain {
// 應(yīng)用使用垃圾回收妈踊,所以不需要 自動(dòng)釋放池 autorelease pool
NSRunLoop *myRunLoop = [NSRunLoop currentRunLoop];
// 創(chuàng)建一個(gè) run loop observer 并且將他添加到當(dāng)前 run loop 中去
/*!
* @author 楊超, 16-01-13 15:01:45
*
* @brief CFRunLoopObserverContext 用來(lái)配置 CFRunLoopObserver 對(duì)象行為的結(jié)構(gòu)體
typedef struct {
CFIndex version;
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
} CFRunLoopObserverContext;
*
* @param version 結(jié)構(gòu)體版本號(hào)了嚎,必須為0
* @param info 一個(gè)程序預(yù)定義的任意指針,可以再 run loop Observer 創(chuàng)建時(shí)為其關(guān)聯(lián)。這個(gè)指針將被傳到所有 context 多定義的所有回調(diào)中歪泳。
* @param retain 程序定義 info 指針的內(nèi)存保留(retain)回調(diào),可以為 NULL
* @param release 程序定義 info 指針的內(nèi)存釋放(release)回調(diào)萝勤,可以為 NULL
* @param copyDescription 程序定于 info 指針的 copy 描述回調(diào),可以為 NULL
*
* @since
*/
CFRunLoopObserverContext context = {0 , (__bridge void *)(self), NULL, NULL, NULL};
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &myRunLoopObserverCallBack, &context);
if (observer) {
CFRunLoopRef cfLoop = [myRunLoop getCFRunLoop];
CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
}
// 創(chuàng)建并安排好 timer
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(doFireTimer) userInfo:nil repeats:YES];
NSInteger loopCount = 10;
do {
// 3秒后運(yùn)行 run loop 實(shí)際效果是每三秒進(jìn)入一次當(dāng)前 while 循環(huán)
[myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
loopCount --;
} while (loopCount);
}
void myRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
NSLog(@"observer正在回調(diào)\n%@----%tu----%@", observer, activity, info);
}
- (void)doFireTimer {
NSLog(@"計(jì)時(shí)器回調(diào)");
}
當(dāng)為一個(gè)長(zhǎng)期存活的現(xiàn)場(chǎng)配置 runloop 時(shí)呐伞,至少添加一個(gè) input source 去接收消息敌卓。盡管你可以?xún)H僅使用一個(gè) 關(guān)聯(lián)的timer 就可以進(jìn)入 run loop,一旦 timer 啟動(dòng)伶氢,通常都會(huì)被作廢掉趟径,這將會(huì)硬氣 run loop 的退出。關(guān)聯(lián)一個(gè)重復(fù)執(zhí)行的 timer 定時(shí)器可以保持讓 runloop 在很長(zhǎng)的一段時(shí)期內(nèi)得以運(yùn)行癣防,但是需要周期性的去啟動(dòng)定時(shí)器 timer 來(lái)喚醒你的線(xiàn)程蜗巧,這是投票有效的另一種形式(這句莫名其妙,不懂是干嗎的)蕾盯。相比之下幕屹, input source 會(huì)等待事件的發(fā)生,并保持線(xiàn)程處于睡眠狀態(tài)直到事件確實(shí)發(fā)生了级遭。
開(kāi)動(dòng) run loop
在應(yīng)用中望拖,只有在子線(xiàn)程中才是有必要開(kāi)啟 run loop 的,一個(gè) run loop 必須至少有一個(gè)用來(lái)監(jiān)視的 input source 装畅。如果一個(gè)關(guān)聯(lián)的都沒(méi)有靠娱,run loop 將會(huì)立即退出。
下面有一些方法開(kāi)啟 run loop:
- 無(wú)條件的
- 通過(guò)一套時(shí)間限制
- 在一個(gè)特別的 mode 下
無(wú)條件的進(jìn)入你的 run loop 是最簡(jiǎn)單的選項(xiàng)掠兄,但這種也是最不可取的。無(wú)條件地運(yùn)行你的 run loop 將會(huì)使你的線(xiàn)程進(jìn)入進(jìn)入永久的循環(huán)中锌雀,這使你很難控制運(yùn)行循環(huán)本身蚂夕。你可以添加和移除 input source 和 timer,但是只有一種方式去停止 run loop感局,那就是將它殺死倦淀。同時(shí)也不存在在自定義 mode 中運(yùn)行 run loop 的方法非迹。
為了替代無(wú)條件的運(yùn)行 run loop ,更好的辦法是使用超時(shí)值來(lái)運(yùn)行 runloop等脂。當(dāng)你使用超時(shí)值時(shí),run loop 會(huì)一直運(yùn)行直到在事件來(lái)臨時(shí) 或者 分配的時(shí)間結(jié)束時(shí)撑蚌。當(dāng)你的事件到達(dá)時(shí)上遥,系統(tǒng)會(huì)分配一個(gè) handler 去處理它,并且之后 run loop 會(huì)退出争涌。你可以用代碼重啟你的 run loop 以便處理下一個(gè)事件粉楚。如果不想繼續(xù)使用剛才分配時(shí)間結(jié)束的原則,也可以簡(jiǎn)單的重啟 runloop 或者使用這些時(shí)間去做任何你需要做的事。
除了使用超時(shí)值模软,你也可以使用指定的 mode 運(yùn)行 run loop伟骨。mode 和超時(shí)值不會(huì)互相排斥,并且都可以用來(lái)啟動(dòng)一個(gè)線(xiàn)程燃异。
表 3-2 展示了一個(gè)線(xiàn)程入口的常用的例行程序携狭。示例代碼的關(guān)鍵部分展示了一個(gè) run loop 的基礎(chǔ)架構(gòu)。本質(zhì)上回俐,你將 input sources 和 timers 添加到你的 runloop 中逛腿,然后重復(fù)的調(diào)用其中一個(gè)例行程序來(lái)啟動(dòng) run loop 。每一次例行程序返回時(shí)鲫剿,你需要檢查一下是否滿(mǎn)足可能會(huì)退出線(xiàn)程的條件鳄逾。示例使用了 Core Foundation 的框架的例行程序以便檢查返回結(jié)果并且可以決定如何退出 runloop。如果你是用的是 Cocoa 灵莲,你也可以使用類(lèi)似的方式通過(guò) NSRunLoop 的方法去運(yùn)行 runloop 雕凹, 并且不需要檢查返回值。(使用 NSRunLoop 的方法的例子可以參考 表3-14.)
表 3-2 運(yùn)行 runloop
- (void)skeletionThreadMain {
// 如果你的應(yīng)用沒(méi)有使用垃圾回收 請(qǐng)?jiān)谶@里添加 自動(dòng)釋放池(ps:這示例代碼也太老了政冻,誰(shuí)還用垃圾回收懊兜帧)
BOOL done = NO;
// 給 runloop 添加 source 或timer,然后做一些其他的配置
do {
// 開(kāi)啟 runloop 并且被一個(gè) source 被處理后要返回
/** SInt32 32位有符號(hào)整數(shù) */
SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);
// 如果 source 已經(jīng)顯式的停止了 runloop 明场,或者根本不存在任何 source 或 timer汽摹,將會(huì)退出。
if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished)) {
done = YES;
// 在這里檢查任何其他符合退出的條件并且按需設(shè)置 done 變量的值苦锨。
}
} while (!done);
// 在這里清除代碼逼泣。確保釋放任何之前創(chuàng)建的自動(dòng)釋放池。
}
可以遞歸開(kāi)啟 runloop舟舒,換句話(huà)說(shuō)拉庶,你可以使用 input source 或者 timer 的例行程序來(lái)調(diào)用 CFRunLoopRun,CFRunLoopRunInMode或者任何 NSRunLoop 的 runloop 啟動(dòng)方法。這樣做你可以使用任何你想用的 mode 來(lái)運(yùn)行一個(gè) 嵌套的 run loop 秃励,包括 通過(guò)外層 run loop 使用的 mode 氏仗。
退出 RunLoop
有兩種途徑可以讓 runloop 在處理事件之前退出:
- 使用超時(shí)值配置 runloop 運(yùn)行。
- 直接告訴 runloop 停止(ps:夺鲜。皆尔。币励。這條太搞了)慷蠕。
使用超時(shí)值無(wú)疑是更偏愛(ài)的方法,如果你能管理它榄审,指定一個(gè)超時(shí)值使 runloop 結(jié)束所有他的正常處理的任務(wù)砌们, 包括在退出前向 runloop observer 發(fā)送通知。
使用 CFRunLoopStop 函數(shù)顯示地停止 runloop,產(chǎn)生的結(jié)果和超時(shí)相似浪感。runloop 會(huì)發(fā)送任何 runloop 提醒通知然后才退出昔头。不同的是你可以將這項(xiàng)技術(shù)應(yīng)用在你用無(wú)條件方式開(kāi)啟的 runloop 上。
盡管移除一個(gè) runloop 的 input source 和 timer 可以造成 runloop 的退出影兽,但這并不是一個(gè)可靠的方式來(lái)停止 runloop 揭斧。一些系統(tǒng)例行程序給 runloop 添加一些 input source 來(lái)處理必要的事件。你的代碼可能無(wú)法看出這些 input source峻堰,你可能不能移除這些用來(lái)防止 runloop 退出的 source讹开。
線(xiàn)程安全 和 Run Loop 對(duì)象
線(xiàn)程安全大多取決于你用來(lái)操作 runloop 的API。Core Foundation 函數(shù) 一般來(lái)說(shuō)都是線(xiàn)程安全的捐名,所以可以被任何線(xiàn)程調(diào)用旦万。假如你正在執(zhí)行一個(gè)修改 runloop 配置的操作,那么繼續(xù)吧镶蹋,對(duì)擁有 runloop 的線(xiàn)程來(lái)說(shuō)這樣做仍然是很好的作法成艘。
Cocoa 的 NSRunLoop
類(lèi)內(nèi)部不像 Core Foundation 中的接口那樣是線(xiàn)程安全的。如果你要使用 NSRunLoop 類(lèi)去修改你的 runloop贺归,你只能在 runloop 所在的線(xiàn)程中這樣做淆两。先其他線(xiàn)程中的 runloop 中添加 input source 或 timer 會(huì)引起你的程序崩潰或出現(xiàn)不可預(yù)知的異常。
配置 run loop source
接下來(lái)的章節(jié)將展示如何在 Cocoa 和 Core Foundation 中設(shè)置不同類(lèi)型的 input source拂酣。
定義一個(gè)自定義自定義 input source
創(chuàng)建一個(gè)自定義的 input source 你需要實(shí)現(xiàn)以下這些條件:
- 你想要你的 source 處理的信息
- 一段調(diào)度模塊的例行程序讓感興趣的客戶(hù)機(jī)了解如何連接你的 input source秋冰。
- 一段處理模塊例行程序用來(lái)處理任何客戶(hù)機(jī)發(fā)送的請(qǐng)求
- 一段取消模塊的例行程序用來(lái)銷(xiāo)毀你的 source
因?yàn)槟銊?chuàng)建了一個(gè)自定義的 input source 來(lái)處理自定義的信息,所以實(shí)際上的配置會(huì)設(shè)計(jì)的非常靈活婶熬。調(diào)度模塊剑勾,處理模塊和取消模塊的例行程序幾乎都是你的自定義 input source 的關(guān)鍵例行程序。剩下的大多數(shù) input source 行為都發(fā)生在這些例行處理程序之外赵颅。比如甥材,由你來(lái)定義一個(gè)工具用來(lái)將數(shù)據(jù)傳到你的 input source并且傳遞你的 input source 的數(shù)據(jù)到其他線(xiàn)程中去。
插圖 3-2 展示了一個(gè)簡(jiǎn)單的自定義 input source 的配置性含。在本例中,應(yīng)用程序主線(xiàn)程維持引用了input source 鸳惯, input source 的緩沖模塊商蕴,還有安裝 input source 的 runloop。當(dāng)主線(xiàn)程有一個(gè)任務(wù)向切換到工作子線(xiàn)程中去芝发,他會(huì)發(fā)送一個(gè)命令绪商,命令緩沖區(qū)以及啟動(dòng)任務(wù)所需的任何線(xiàn)程的信息(因?yàn)橹骶€(xiàn)程和工作子線(xiàn)程的 input source 都有權(quán)限去訪問(wèn)命令緩沖區(qū),訪問(wèn)必須同步)一旦命令發(fā)送了辅鲸,主線(xiàn)程會(huì)發(fā)送信號(hào)給 input source 來(lái)喚醒工作子線(xiàn)程的 runloop格郁。一旦受到喚醒的命令, runloop 會(huì)調(diào)用 input source 的處理程序 去處理命令緩存器中緩存的命令。
圖 3-2 操作一個(gè)自定義 input source
接下來(lái)的章節(jié)將會(huì)解釋如何通過(guò)上圖實(shí)現(xiàn)一個(gè)自定義 input source 并展示你需要實(shí)現(xiàn)的關(guān)鍵代碼例书。
定義 input source
定義一個(gè)自定義 input source 需要使用 Core Foundation 的例行程序配置你的 runloop input source 并且 將它與你的 runloop 關(guān)聯(lián)锣尉。盡管基礎(chǔ)處理程序是基于 C-語(yǔ)言 函數(shù)的,但這不會(huì)阻止你使用 Objective-C 或者 C++ 去封裝它為面向?qū)ο蟮拇a决采。
插圖3-2中介紹的 input source 使用一個(gè) objective-C 對(duì)象去管理一個(gè)命令緩存器自沧,并與 runloop 進(jìn)行協(xié)調(diào)。列表3-3 展示了這個(gè)對(duì)象的定義树瞭。RunLoopSource
對(duì)象管理一個(gè)命令緩沖器拇厢,并且使用命令緩存器接受來(lái)自其他線(xiàn)程的消息。該表也展示了 RunLoopContext
對(duì)象的定義晒喷,該對(duì)象僅僅是一個(gè)容器孝偎,用來(lái)傳遞一個(gè) RunLoopSource
對(duì)象和應(yīng)用主線(xiàn)程的 runloop 引用。
表 3-3 自定義 input source 對(duì)象的定義
@interface YCRunLoopSource : NSObject
{
CFRunLoopSourceRef runLoopSource;
NSMutableArray *commands;
}
- (id)init;
// 添加
- (void)addToCurrentRunLoop;
// 銷(xiāo)毀
- (void)invalidate;
// 處理方法
- (void)sourceFired;
// 用來(lái)注冊(cè)需要處理的命令的客戶(hù)機(jī)接口
- (void)addCommand:(NSInteger)command withData:(id)data;
- (void)fireAllCommandsOnRunLoop:(CFRunLoopSourceRef)runloop;
// 這些是CFRunLoopRef 的回調(diào)函數(shù)
/** 調(diào)度函數(shù) */
void RunLoopSourceScheduleRoutine(void *info, CFRunLoopRef r1, CFStringRef mode);
/** 處理函數(shù) */
void RunLoopSourcePerformRoutine (void *info);
/** 取消函數(shù) */
void RunLoopSourceCancelRoutine (void *info, CFRunLoopRef rl, CFStringRef mode);
@end
// RunLoopContext 是一個(gè) 在注冊(cè) input source 時(shí)使用的容器對(duì)象
@interface YCRunLoopContext : NSObject
{
CFRunLoopRef runLoop;
YCRunLoopSource *source;
}
/** 持有 runloop 和 source */
@property (readonly) CFRunLoopRef runLoop;
@property (readonly) YCRunLoopSource *source;
- (id)initWithSource:(YCRunLoopSource*)src andLoop:(CFRunLoopRef)loop;
@end
盡管 Objective-C 代碼管理著 input source 的自定義數(shù)據(jù)凉敲。關(guān)聯(lián)一個(gè) input source 到一個(gè)具備 基于 C-語(yǔ)言 的回調(diào)函數(shù)的 runloop 衣盾。其中第一個(gè)函數(shù)是當(dāng)你實(shí)際將 input source 添加到 runloop 中的時(shí)刻調(diào)用。流程將展示在 表 3-4 中荡陷。因?yàn)檫@個(gè) input source 僅只有一個(gè) 客戶(hù)機(jī)(主線(xiàn)程)雨效。它使用調(diào)度者函數(shù)通過(guò)目標(biāo)線(xiàn)程 application 的代理發(fā)送消息在目標(biāo)線(xiàn)程注冊(cè)自己。當(dāng) application 的代理和 input source 進(jìn)行通信時(shí) ,會(huì)使用 RunLoopContext 對(duì)象中的 info
信息來(lái)完成這個(gè)事废赞。
表 3-4 調(diào)度 run loop source
void RunLoopSourceScheduleRoutine(void *info, CFRunLoopRef r1, CFStringRef mode){
YCRunLoopSource *obj = (__bridge YCRunLoopSource *)info;
// 這里的 Appdelegate 是主線(xiàn)程的代理
AppDelegate *del = [AppDelegate sharedAppDelegate];
// 上下文對(duì)象中持有source自己
YCRunLoopContext *theContext = [[YCRunLoopContext alloc] initWithSource:obj andLoop:r1];
// 通過(guò)代理去注冊(cè) Source 自己
[del performSelectorOnMainThread:@selector(registerSource:) withObject:theContext waitUntilDone:NO];
}
其中最重要的回調(diào)例行程序是當(dāng)你的 input source 被信號(hào)激活時(shí)處理自定義數(shù)據(jù)的部分徽龟。表3-5中展示了與 RunLoopSource
對(duì)象關(guān)聯(lián)的執(zhí)行者回調(diào)例行程$序,這個(gè)函數(shù)僅僅轉(zhuǎn)發(fā)用來(lái) sourceFired
方法工作的請(qǐng)求,該請(qǐng)求用來(lái)處理任何 command buffer
(命令緩沖區(qū))中存在的命令唉地。
表3-5 input source 中的執(zhí)行者
void RunLoopSourcePerformRoutine (void *info)
{
RunLoopSource* obj = (RunLoopSource*)info;
[obj sourceFired];
}
如果你使用 CFRunLoopSourceInvalidate
函數(shù)將 input source 從 runloop 重移除据悔。系統(tǒng)會(huì)調(diào)用你的 input source 中的取消者例行程序。你可以利用這個(gè)例行程序去通知客戶(hù)機(jī)你的 input source 不再可用并且他們應(yīng)該移除任何自己的相關(guān)的引用耘沼。表3-6 展示了取消者例行回調(diào)程序通過(guò) RunLoopSource 對(duì)象進(jìn)行注冊(cè)极颓。這個(gè)函數(shù)發(fā)送另一個(gè) RunLoopContext 對(duì)象給 application 代理。但是這讓代理去移除 runloop surce 的相關(guān)引用群嗤。
表3-6 銷(xiāo)毀一個(gè) input source
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];
}
筆記:應(yīng)用代理方法 registerSource: 和 removeSource 方法在下面的章節(jié) 《協(xié)調(diào) input source 的客戶(hù)機(jī)》展示
為 runloop 安裝 input source
表3-7 展示了 RunLoopSource
類(lèi)的 init
方法 和 addToCurrentRunLoop
方法菠隆。init
方法創(chuàng)建了 CFRunLoopSource 不透明類(lèi)型的必須關(guān)聯(lián)到 runloop 的對(duì)象。它會(huì)傳遞 RunLoopSource
對(duì)象自己作為 山下文信息 以便于例行回調(diào)程序有一個(gè)指向?qū)ο蟮闹羔樋衩亍nput source 直到線(xiàn)程喚起 addToCurrentRunLoop
方法時(shí)才會(huì)執(zhí)行安裝骇径,準(zhǔn)確將在 RunLoopSourceScheduleRoutine 回調(diào)函數(shù)調(diào)用時(shí)。 一旦 input source 安裝到 runloop 中者春,線(xiàn)程將會(huì)運(yùn)行自己的 runloop 去等待 input source 發(fā)出事件破衔。
表3-7 安裝 run loop source
- (id)init {
// 創(chuàng)建上下文容器,其中會(huì)連接自己的 info钱烟,retain info release info晰筛,還會(huì)關(guān)聯(lián)三個(gè)例行程序嫡丙。
CFRunLoopSourceContext context = {0, (__bridge void *)(self), NULL, NULL, NULL ,NULL, NULL, &RunLoopSourceScheduleRoutine, RunLoopSourceCancelRoutine, RunLoopSourcePerformRoutine};
/** 通過(guò)索引,上下文读第,和CFAllocator創(chuàng)建source */
runLoopSource = CFRunLoopSourceCreate(NULL, 0, &context);
commands = [[NSMutableArray alloc] init];
return self;
}
- (void)addToCurrentRunLoop{
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopDefaultMode);
}
協(xié)調(diào) input source 的客戶(hù)機(jī)
對(duì)于你的 input source 會(huì)非常有用曙博,你需要操作它并且從其他線(xiàn)程向它提供消息。input source 的要點(diǎn)是將其添加到線(xiàn)程并睡眠直到有事情要做時(shí)才喚醒卦方。事實(shí)上很有必要讓其他線(xiàn)程了解 input surce 并且有方法可以和它交流(溝通數(shù)據(jù))羊瘩。
通知你的 input source 客戶(hù)機(jī)的方法之一是發(fā)出注冊(cè)請(qǐng)求 當(dāng)你的 input source 第一次安裝到你的 runloop 中時(shí)。你可以向你的 input source 注冊(cè)盡可能多的客戶(hù)機(jī)盼砍〕韭穑或者你僅僅只是簡(jiǎn)單的用一些中央機(jī)構(gòu),然后將你的 input source 聲明為感興趣的客戶(hù)端進(jìn)行注冊(cè)浇坐。表3-8 展示了 通過(guò)代理 和 調(diào)用喚起定義的 注冊(cè)方法 當(dāng) RunLoopSource 對(duì)象的調(diào)度者函數(shù)被調(diào)用時(shí)睬捶。這個(gè)方法將會(huì)收到 RunLoopSource 提供的 RunLoopContext 對(duì)象并且將它添加到他的 source 列表中。這個(gè)表也會(huì)展示 當(dāng) input source 從 他的 runloop 中被移除時(shí) 用來(lái)注銷(xiāo)的例行程序近刘。
表 3-8 使用 application 的 代理 注銷(xiāo)并且移除 input source
#import "YCRunLoopSource.h"
#import "YCRunLoopContext.h"
@interface AppDelegate : NSObject
@property (nonatomic, strong) NSMutableArray *sourcesToPing;
/** 應(yīng)該是一個(gè)單例 */
+ (instancetype)sharedAppDelegate;
- (void)registerSource:(YCRunLoopContext *)context;
- (void)removeSource:(YCRunLoopContext *)context;
@end
static AppDelegate *_instance;
@implementation AppDelegate
+ (instancetype)sharedAppDelegate
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (void)registerSource:(YCRunLoopContext *)context
{
[self.sourcesToPing addObject:context];
}
- (void)removeSource:(YCRunLoopContext *)context
{
id objToRemove = nil;
for (YCRunLoopContext *contextObj in self.sourcesToPing) {
if ([contextObj isEqual:context]) {
objToRemove = contextObj;
break;
}
}
if (objToRemove) {
[self.sourcesToPing removeObject:objToRemove];
}
}
- (NSMutableArray *)sourcesToPing {
if (_sourcesToPing == nil) {
_sourcesToPing = @[].mutableCopy;
}
return _sourcesToPing;
}
@end
Note:回調(diào)函數(shù)會(huì)在之前的表3-4和3-6中調(diào)用這些函數(shù)
信號(hào)激活 input source
釋放 input source 的數(shù)據(jù)之后擒贸,客戶(hù)機(jī)必須發(fā)信號(hào)給 source 并且喚醒它的 runloop。發(fā)信號(hào)給 source 是讓 runloop 知道 source 已經(jīng)準(zhǔn)備好被處理觉渴。因?yàn)榫€(xiàn)程可能會(huì)在發(fā)信號(hào)的時(shí)處于睡眠狀態(tài)介劫,所以那你必須顯式的讓 run loop 保持喚醒。除非如此案淋,不然在處理 input source 時(shí)會(huì)出現(xiàn)延遲座韵。
表 3-9 展示了 RunLoopSource
對(duì)象的 fireCommandsOnRunLoop
方法,客戶(hù)機(jī)會(huì)在它準(zhǔn)備好為 source 處理添加到 buffer 緩沖區(qū)中的 command 命令時(shí)調(diào)用這個(gè)方法踢京。
表 3-9 喚醒 run loop
- (void)fireCommandsOnRunLoop:(CFRunLoopRef)runloop
{
CFRunLoopSourceSignal(runLoopSource);
CFRunLoopWakeUp(runloop);
}
Note:你不能通過(guò)向一個(gè)自定義 input source 發(fā)信息來(lái)處理一個(gè) SIGHUP 或者其他處理類(lèi)型的信號(hào)
誉碴,Core Foundation 框架中用于喚醒 runloop 的函數(shù)不是信號(hào)安全的。并且不能作為你的應(yīng)用程序中內(nèi)置信號(hào)處理的例行程序使用瓣距。關(guān)于更多的關(guān)于信號(hào)處理程序黔帕,詳見(jiàn) sigaction man 頁(yè)面。
配置 Timer Source
為了創(chuàng)建 timer source蹈丸,所有你需要做的就是創(chuàng)建一個(gè) timer 對(duì)象成黄,并且在你的 run loop 中調(diào)度它。在 Cocoa 中逻杖,你使用 NSTimer 類(lèi)來(lái)創(chuàng)建一個(gè)新的 timer 對(duì)象慨默。在 Core Foundation 框架中,你可以使用 CFRunLoopTimerRef 不透明類(lèi)型來(lái)創(chuàng)建弧腥。NSTimer
類(lèi)只是 Core Foundation 框架中的一個(gè)擴(kuò)展,是用來(lái)方便的提供一些功能潮太,比如使用相同的方法創(chuàng)建和調(diào)度 timer 管搪。
在 Cocoa 中虾攻,你能通過(guò)以下兩種類(lèi)方法創(chuàng)建和調(diào)度 timer。
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
scheduledTimerWithTimeInterval:invocation:repeats:
這些方法創(chuàng)建 timer 并且將它們添加到當(dāng)前線(xiàn)程的 run loop 中的 default mode(NSDefaultRunLoopMode) 中去更鲁。如果你使用的是 NSTimer 對(duì)象霎箍,那就可以手動(dòng)調(diào)度 timer 并且可以使用 NSRunLoop 的 addTimer:forMode: 手動(dòng)將它添加到 runloop 中去。這兩種技術(shù)都是基于同一種澡为,但是通過(guò)timer 的配置給你不同級(jí)別的控制漂坏。比如你手動(dòng)創(chuàng)建 timer 并將它添加到 run loop 中,并添加到除 default mode 之外的其他 mode 中去媒至。表3-10 展示了如何使用兩種技術(shù)創(chuàng)建 timer顶别。第一個(gè) timer 初始化為 延遲一秒但是會(huì)在延遲后有規(guī)律的每個(gè)0.1秒觸發(fā)一次。第二個(gè) timer 會(huì)在 0.2 秒延遲后開(kāi)始觸發(fā)拒啰,并且在延遲結(jié)束后 每 0.2 秒觸發(fā)一次驯绎。
表3-10 使用 NSTimer 創(chuàng)建和調(diào)度 timer
NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
// 創(chuàng)建并調(diào)度第一個(gè) 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];
// 創(chuàng)建并調(diào)動(dòng)第二個(gè) timer
[NSTimer scheduledTimerWithTimeInterval:0.2
target:self
selector:@selector(myDoFireTimer2:)
userInfo:nil
repeats:YES];
表3-11 展示了使用 Core Foundation 框架時(shí)需要配置的代碼。盡管實(shí)例代碼中沒(méi)有傳遞任何用戶(hù)自定義的信息的上下文結(jié)構(gòu)谋旦,但是你可以使>用這個(gè)結(jié)構(gòu)去傳遞任何你的 timer 所需要自定義數(shù)據(jù)剩失。關(guān)于更多該結(jié)構(gòu)的內(nèi)容可以瀏覽 CFRunLoopTimer 參考。
表 3-11 使用 Core Foundation 框架創(chuàng)建和調(diào)度一個(gè) timer
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);
配置一個(gè)基于 port 的 input source
Cocoa 和 Core Foundation 都支持用于和線(xiàn)程間或者進(jìn)程間通信的基于 端口的對(duì)象册着。接下來(lái)的章節(jié)將會(huì)向你展示如何使用一些不同類(lèi)型的 port 構(gòu)建 port 通信拴孤。
配置一個(gè)NSMachPort Object
使用 NSMachPort 對(duì)象創(chuàng)建一個(gè)本地連接。你創(chuàng)建一個(gè) port 對(duì)象并把它添加到你的主線(xiàn)程 run loop 中去甲捏。當(dāng)啟動(dòng)你的子線(xiàn)程時(shí)演熟,你要傳一些相同的對(duì)象到你的線(xiàn)程入口點(diǎn)函數(shù)中去。子線(xiàn)程可以使用相同的對(duì)象發(fā)送信息回到你的主線(xiàn)程中去摊鸡。
實(shí)現(xiàn)主線(xiàn)程代碼
表 3-12 中展示了用于啟動(dòng)子工作線(xiàn)程的主線(xiàn)程代碼绽媒。因?yàn)?Cocoa 框架執(zhí)行很多介入步驟用于配置 port 和 run loop ,Cocoa 的 launchThread
方法相比于 Core Foundation 的等價(jià)功能表 3-17更加簡(jiǎn)潔明了免猾。盡管如此是辕,這兩個(gè)框架在這一模塊的功能表現(xiàn)基本都是相同的。其中一個(gè)存在的差異是與發(fā)送本地 port 到工作線(xiàn)程的方式不同猎提,這個(gè)方法是直接發(fā)送 NSPort 對(duì)象的获三。
表 3-12 list3-12 Main Thread lauch method
- (void)launchThread {
NSPort *myPort = [NSMachPort port];
if (myPort) {
// 這個(gè)類(lèi)處理即將過(guò)來(lái)的 port 信息
[myPort setDelegate:self];
// 將此端口作為 input source 安裝到當(dāng)前 run loop 中去
[[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode];
// 開(kāi)啟工作子線(xiàn)程,讓工作子線(xiàn)程去釋放 port
[NSThread detachNewThreadSelector:@selector(LaunchThreadWithPort:) toTarget:[MyWorkerClass class] withObject:myPort];
}
}
為了設(shè)置為線(xiàn)程間雙向通信信
道锨苏,在
登記信息中疙教,你需要讓工作線(xiàn)程發(fā)送自己的本地 port 到主線(xiàn)程。接收登記信息是為了讓你的主線(xiàn)程知道開(kāi)動(dòng)子線(xiàn)程的過(guò)程進(jìn)行的非常順利伞租,同時(shí)也為我們?yōu)樘峁┝艘环N方法去向該線(xiàn)程發(fā)送更多信息贞谓。
表 3-13 展示了用于主線(xiàn)程的handlePortMessage:方法,這個(gè)方法會(huì)在線(xiàn)程到達(dá)自己的本地 port 時(shí)進(jìn)行調(diào)用葵诈。當(dāng)?shù)怯浶畔?check-in message)到達(dá)時(shí)裸弦,該方法將直接從 port 信息中檢索子線(xiàn)程的 port 并保存以備后用祟同。
表 3-13 處理 Mach port 信息
# define kCheckinMessage 100
// 處理工作線(xiàn)程的響應(yīng)的代理方法
- (void)handlePortMessage:(NSPortMessage *)portMessage
{
unsigned int message = [portMessage msgid];
// 定義遠(yuǎn)程端口
NSPort *distantPort = nil;
if (message == kCheckinMessage) {
// 獲取工作線(xiàn)程的通信 port
distantPort = [portMessage sendPort];
// 引用計(jì)數(shù)+1 并 保存工作端口以備后用
[self storeDistantPort:distantPort];
} else {
// 處理其他信息
}
}
- (void)storeDistantPort:(NSPort *)port {
// 保存遠(yuǎn)程端口
}
實(shí)現(xiàn)子線(xiàn)程代碼
對(duì)于工作子線(xiàn)程,你必須配置它并且是使用指定的端口進(jìn)行信息溝通并返回到主線(xiàn)程理疙。
表 3-14 展示了用于設(shè)置工作線(xiàn)程的代碼晕城。在創(chuàng)建一個(gè) qutorealease pool 之后,該方法會(huì)創(chuàng)建一個(gè)工作對(duì)象去驅(qū)動(dòng)線(xiàn)程執(zhí)行窖贤。該工作對(duì)象 的 sendCheckinMessage:
方法(表3-15 所示)為工作線(xiàn)程創(chuàng)建一個(gè)本地端口然后回復(fù)一個(gè) check-in 信息給主線(xiàn)程砖顷。
表 3-14 使用 Mach port 啟動(dòng)子線(xiàn)程
+(void)LaunchThreadWithPort:(id)inData
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// 設(shè)置本線(xiàn)程與主線(xiàn)程的連接
NSPort* distantPort = (NSPort*)inData;
MyWorkerClass* workerObj = [[self alloc] init];
[workerObj sendCheckinMessage:distantPort];
[distantPort release];
// 讓 run loop 處理這些邏輯
do
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}
while (![workerObj shouldExit]);
[workerObj release];
[pool release];
}
當(dāng)使用 NSMachPort
時(shí),本地和遠(yuǎn)端線(xiàn)程都可以使用相同的 port 對(duì)象 完成線(xiàn)程之間的單工通信(單向通信)。換句話(huà)說(shuō)粤攒,通過(guò)一個(gè)線(xiàn)程創(chuàng)建的本地對(duì)象會(huì)成為另一個(gè)線(xiàn)程的遠(yuǎn)端 port 對(duì)象晴弃。(ps:現(xiàn)在總算明白本地就是當(dāng)前線(xiàn)程環(huán)境芍阎,遠(yuǎn)端就是其他線(xiàn)程環(huán)境)。
表 3-15展示了子線(xiàn)程的 check-in 例行程序 (登記信息例行程序)柿隙。這個(gè)方法設(shè)置了他自己的用于和以后進(jìn)行通訊的本地端口叶雹。并且回復(fù)一個(gè) check-in 登記信息給主線(xiàn)程。該方法使用 port 對(duì)象去接收 LaunchThreadWithport:
方法作為信息目標(biāo)满着。
表 3-15 使用 Mach port 發(fā)送 check-in 登記信息
// Worker thread check-in method
- (void)sendCheckinMessage:(NSPort*)outPort
{
// 保留(retain)并保存遠(yuǎn)端的 port 以備后用
[self setRemotePort:outPort];
// 創(chuàng)建和配置工作線(xiàn)程的端口(ps:當(dāng)前線(xiàn)程端口)
NSPort* myPort = [NSMachPort port];
[myPort setDelegate:self];
[[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode];
// 創(chuàng)建 check-in 登記信息
NSPortMessage* messageObj = [[NSPortMessage alloc] initWithSendPort:outPort
receivePort:myPort components:nil];
if (messageObj)
{
// 完成配置信息 并 立即發(fā)送出去
[messageObj setMsgId:setMsgid:kCheckinMessage];
[messageObj sendBeforeDate:[NSDate date]];
}
配置一個(gè) NSMessagePort 對(duì)象
如果想要使用 [NSMessagePort](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSM essagePort_Class/index.html#//apple_ref/occ/cl/NSMessagePort) 對(duì)象創(chuàng)建一個(gè)本地連接谦炒,你不能在線(xiàn)程間僅僅值傳遞一個(gè) port 對(duì)> 象。遠(yuǎn)端信息端口必須通過(guò)名字獲取风喇。
在Cocoa中宁改,如果你想實(shí)現(xiàn)這個(gè)功能,需要使用一個(gè)指定的名字去注冊(cè)你的本地端口魂莫,然后向遠(yuǎn)端線(xiàn)程傳遞注冊(cè)的名字以便于他可以包含一
個(gè)合適的端口對(duì)象用于交流还蹲。表 3-16 展示了 port 創(chuàng)建方法和注冊(cè)方法 用于你想要使用 消息端口(message port)的地方。
表 3-16 注冊(cè)一個(gè) message port
NSPort* localPort = [[NSMessagePort alloc] init];
// 配置對(duì)象并將它添加到當(dāng)前 run loop 中去
[localPort setDelegate:self];
[[NSRunLoop currentRunLoop] addPort:localPort forMode:NSDefaultRunLoopMode];
// 使用指定的名字注冊(cè)端口耙考。名字必須唯一谜喊。
NSString* localPortName = [NSString stringWithFormat:@"MyPortName"];
[[NSMessagePortNameServer sharedInstance] registerPort:localPort
name:localPortName];
在 Core Foundation 框架中配置一個(gè)基于端口的(Port-Based) input source
這個(gè)小結(jié)描述了如歌使用 Core Foundation 框架在你的應(yīng)用的主線(xiàn)程和輔助線(xiàn)程(worker thread)中創(chuàng)建一個(gè)雙向通信信道。
如表3-17 所示為應(yīng)用主線(xiàn)程啟動(dòng)輔助線(xiàn)程所使用的代碼倦始。首先要做的是創(chuàng)建 CFMessagePortRef 不透明對(duì)象去監(jiān)聽(tīng)從輔助線(xiàn)程發(fā)來(lái)的消息斗遏。輔助線(xiàn)程需要用來(lái)創(chuàng)建連接的端口名,以便于字符串值可以被發(fā)送到輔助線(xiàn)程的入口點(diǎn)函數(shù)楣号。端口名在當(dāng)前用戶(hù)的上線(xiàn)文中通常必須是唯一的最易。否則,可能會(huì)出現(xiàn)運(yùn)行沖突炫狱。
表 3-17 給新線(xiàn)程關(guān)聯(lián)一個(gè) Core Foundation message port
#define kThreadStackSize (8 *4096)
OSStatus MySpawnThread()
{
// 創(chuàng)建一個(gè)本地端口用于接受響應(yīng)
CFStringRef myPortName;
CFMessagePortRef myPort;
CFRunLoopSourceRef rlSource;
CFMessagePortContext context = {0, NULL, NULL, NULL, NULL};
Boolean shouldFreeInfo;
// 用端口名 創(chuàng)建一個(gè)符合規(guī)范的字符串
myPortName = CFStringCreateWithFormat(NULL, NULL, CFSTR("com.myapp.MainThread"));
// 創(chuàng)建端口
myPort = CFMessagePortCreateLocal(NULL,
myPortName,
&MainThreadResponseHandler,
&context,
&shouldFreeInfo);
if (myPort != NULL)
{
// 端口已經(jīng)被成功創(chuàng)建
// 現(xiàn)在為他創(chuàng)建 run loop source
rlSource = CFMessagePortCreateRunLoopSource(NULL, myPort, 0);
if (rlSource)
{
// 為當(dāng)前 run loop 添加 source
CFRunLoopAddSource(CFRunLoopGetCurrent(), rlSource, kCFRunLoopDefaultMode);
// 一旦安裝結(jié)束藻懒,這些資源需要被釋放
CFRelease(myPort);
CFRelease(rlSource);
}
}
// 創(chuàng)建線(xiàn)程并且繼續(xù)處理任務(wù)
MPTaskID taskID;
return(MPCreateTask(&ServerThreadEntryPoint,
(void*)myPortName,
kThreadStackSize,
NULL,
NULL,
NULL,
0,
&taskID));
}
如果 port 端口已經(jīng)被安裝并且線(xiàn)程已經(jīng)啟動(dòng),主線(xiàn)程就可以繼續(xù)定期的執(zhí)行去等待輔助線(xiàn)程的 check-in 登記信息视译。一旦 check-in 登記信息到達(dá)嬉荆,它將會(huì)被指派到主線(xiàn)程的 MainThreadResponseHandler
函數(shù)中,如表 3-18 所示酷含,這個(gè)函數(shù)提取輔助線(xiàn)程的端口名并且創(chuàng)建通信管道鄙早。
表 3-18 接收 check-in 登記信息
#define kCheckinMessage 100
// 主線(xiàn)程端口信息處理函數(shù)
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);
// 你必須通過(guò)一個(gè) port 名獲取遠(yuǎn)端信息
messagePort = CFMessagePortCreateRemote(NULL, (CFStringRef)threadPortName);
if (messagePort)
{
// 保留并保存線(xiàn)程的 comm 端口 以備后用
AddPortToListOfActiveThreads(messagePort);
// 如果端口在先前的 函數(shù) 中保留了(retain)汪茧,在這里釋放資源
CFRelease(messagePort);
}
// 釋放資源
CFRelease(threadPortName);
CFAllocatorDeallocate(NULL, buffer);
}
else
{
// 處理其他信息
}
return NULL;
}
主線(xiàn)程配置完成后,唯一要做的就是為新創(chuàng)建的輔助線(xiàn)程創(chuàng)建它自己的 端口和 登記自己的 message限番。表 3-19 所示為輔助線(xiàn)程的入口點(diǎn)函數(shù)舱污。函數(shù)提取了主線(xiàn)程的 port 名并且用它創(chuàng)建了一個(gè)遠(yuǎn)端的連接回復(fù)主線(xiàn)程。之后函數(shù)為自己創(chuàng)建一個(gè)本地端口弥虐,將端口 port 安裝到線(xiàn)程的 runloop 中去扩灯,然后給主線(xiàn)程發(fā)送一個(gè)包含本地端口名的 check-in 登記信息。
OSStatus ServerThreadEntryPoint(void* param)
{
// 創(chuàng)建連接到主線(xiàn)程的遠(yuǎn)端端口
CFMessagePortRef mainThreadPort;
CFStringRef portName = (CFStringRef)param;
mainThreadPort = CFMessagePortCreateRemote(NULL, portName);
// 釋放被用于參數(shù)傳遞的字符串
CFRelease(portName);
// 為輔助才女創(chuàng)建一個(gè)本地端口
CFStringRef myPortName = CFStringCreateWithFormat(NULL, NULL, CFSTR("com.MyApp.Thread-%d"), MPCurrentTaskID());
// 保存線(xiàn)程上下文信息中的端口霜瘪,以便之后使用珠插。
CFMessagePortContext context = {0, mainThreadPort, NULL, NULL, NULL};
Boolean shouldFreeInfo;
Boolean shouldAbort = TRUE;
CFMessagePortRef myPort = CFMessagePortCreateLocal(NULL,
myPortName,
&ProcessClientRequest,
&context,
&shouldFreeInfo);
if (shouldFreeInfo)
{
// 如果不能創(chuàng)建本地端口,則殺死線(xiàn)程
MPExit(0);
}
CFRunLoopSourceRef rlSource = CFMessagePortCreateRunLoopSource(NULL, myPort, 0);
if (!rlSource)
{
// 如果不能創(chuàng)建本地端口颖对,則殺死線(xiàn)程
MPExit(0);
}
// 給 runloop 添加source
CFRunLoopAddSource(CFRunLoopGetCurrent(), rlSource, kCFRunLoopDefaultMode);
// 一旦線(xiàn)程安裝完畢捻撑,這些資源需要釋放
CFRelease(myPort);
CFRelease(rlSource);
// 打包端口名,并發(fā)送 check-in 信息缤底。
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);
// 清除線(xiàn)程數(shù)據(jù)結(jié)構(gòu)
CFRelease(outData);
CFAllocatorDeallocate(NULL, buffer);
// 進(jìn)入 runloop
CFRunLoopRun();
}
一旦進(jìn)入 runloop顾患,所有發(fā)送給線(xiàn)程端口的事件會(huì)被 ProcessClientRequest
函數(shù)處理。該函數(shù)的實(shí)現(xiàn)依賴(lài)于工作線(xiàn)程的類(lèi)型训堆,這里暫不做介紹描验。