Re:從零開始的RunLoop實踐
本系列文章,因我在網上看了很多RunLoop的文章之后(先膜拜各路大牛),感覺自己大概懂了,但是說實戰(zhàn)一下,又無從下碼,本著寫不出來代碼,會再多理論好比有槍開不出子彈,所以盡量以解決開發(fā)中實際問題為出發(fā)點,主要以網絡上的博客和Github找到的代碼為基礎(這都是大牛的功勞),總結出用以實戰(zhàn)的幾個demo,主要為了以后自己使用查找方便(所以此系列提及的理論巨少,基本都是代碼,觀眾老爺也可以直接復制粘貼使用起來),公布在網絡上,歡迎各位指出錯誤,幫助本人及觀看文章的大家成長學習。
Re:從零開始的Runloop實踐02-使用ports 或custom input sources 和其他線程通信
在Re:從零開始的RunLoop實踐01中,使用過- (void)performSelector:(SEL)aSelector onThread 這個方法來實現線程間的通信,這個方法非常好用,常用,但不是今天的主角。
本小節(jié)部分代碼搬運修改自[http://weibo.com/1794363822/CvDEHwuvX?type=comment](http://weibo.com/1794363822/CvDEHwuvX?type=comment)大牛的Github
本篇的Demo地址在:https://github.com/zyzhangyu/RunLoopDemo
建議直接看代碼,雖然寫這么一篇博文,也要個把小時,但是能直接看代碼,最好還是直接看代碼。
custom input sources的核心代碼如下:
CCRunLoopContext類 容器類,保存?zhèn)鬟fRunLoop和InputSource

CCRunLoopInputSource類


處理事件的方法與其他線程注冊的方法是可以自由設置的,核心代碼就是創(chuàng)建一個CFRunLoopSourceRef,并把它加入到一個RunLoop中。理論如下:
CFRunLoopSourceRef 是事件產生的地方。Source有兩個版本:Source0 和 Source1。
? Source0 只包含了一個回調(函數指針),它并不能主動觸發(fā)事件。使用時,你需要先調用 CFRunLoopSourceSignal(source),將這個 Source 標記為待處理,然后手動調用 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop,讓其處理這個事件。
創(chuàng)建自己的CFRunLoopSourceRef需要一個CFRunLoopSourceContext,下面是CFRunLoopSourceContext的定義,在init方法中有使用的實例:
typedef struct {
CFIndex version;
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
Boolean (*equal)(const void *info1, const void *info2);
CFHashCode (*hash)(const void *info);
void (*schedule)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
void (*cancel)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
void (*perform)(void *info);
} CFRunLoopSourceContext;
我們可以看到,最后有三個回調,這三個回調也是非常重要的,
//當source添加進runloop的時候,調用此回調方法,將源注冊到其他線程 <== CFRunLoopAddSource(runLoop, source, mode);
void (*schedule)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
// 如果使用CFRunLoopSourceInvalidate函數把輸入源從Run Loop里面移除的話,系統會回調該方法。
// 我們在該方法中移除了主線程對當前Input source context的引用。'
void (*cancel)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
// 當前Input source被告知需要處理事件的回調方法
void (*perform)(void *info);
在本小節(jié)的demo中,schedule被用來把runLoopContext傳遞給主線程,在主線程之中有一個數組用來收集我們主動創(chuàng)建的RunLoop。cancel和schedule一樣,區(qū)別在于通知主線程移除指定的RunLoop,perform最為重要,我們把各個線程發(fā)送到指定RunLoop線程中的任務,在此執(zhí)行。這里不好理解,建議大家查看本文的配套代碼食用更佳。
下載地址:https://github.com/zyzhangyu/RunLoopDemo

好了,知道這些,現在來看一個進行線程通信的實例:
第一步:
創(chuàng)建CCRunLoopCustomInputSourceThread類,繼承自NSThread重寫main方法,使用上面的CCRunLoopInputSource類,添加觀察者,開啟runLoop循環(huán)。
CCRunLoopCustomInputSourceThread.h
https://github.com/zyzhangyu/RunLoopDemo/blob/master/ZYRunLoopDemo02/ZYRunLoopDemo02/CCRunLoopCustomInputSourceThread.h
CCRunLoopCustomInputSourceThread.m
https://github.com/zyzhangyu/RunLoopDemo/blob/master/ZYRunLoopDemo02/ZYRunLoopDemo02/CCRunLoopCustomInputSourceThread.m
這一步的核心代碼:
self.customInputSource = [[CCRunLoopInputSource alloc] init];
self.customInputSource.delegate = self;
[self.customInputSource addToCurrentRunLoop];
執(zhí)行addToCurrentRunLoop時,會觸發(fā)上面圖片的第一個回調。
void runLoopSourceScheduleRoutine (void info, CFRunLoopRef runLoopRef, CFStringRef mode)
會把這個線程的runloop和InputSource(通過上面的容器類CCRunLoopContext)傳遞給主線程。
第二步:
開啟此線程就ok了,此刻RunLoop會在kCFRunLoopBeforeWaiting狀態(tài),等待事件喚醒。

第三步:
在主線程觸發(fā)調用Custom Input Source的方法,會觸發(fā)上述三個回調中的runLoopSourcePerformRoutine





demo 下載地址:https://github.com/zyzhangyu/RunLoopDemo
接下來是使用port通信的實例(port通信這部分的原理,我也不是很懂,資料很少,也想不到具體使用的場景,下面的demo改編自網絡上找到的blog):
port通信



Log記錄:

demo 下載地址:https://github.com/zyzhangyu/RunLoopDemo
本篇和上篇的博文Re:從零開始的RunLoop實踐01的Log可以對比一下,能發(fā)現一些問題,再上一次這張圖:

參考博客:
http://www.itdecent.cn/p/4d5b6fc33519
http://blog.ibireme.com/2015/05/18/runloop/