developer:hi,runloop 初次見(jiàn)面,交個(gè)朋友吧?
runloop?。耗愫茫芨吲d認(rèn)識(shí)你!
developer:我先自我介紹一下,我叫iOS Developer,你呢?
runloop?。何医衦unloop,你的有些同伴叫我跑圈,我覺(jué)得叫我循環(huán)運(yùn)行比較合適!
developer:我聽(tīng)我的同伴說(shuō)你有點(diǎn)不是很好相處啊,是不是真的?
runloop?。翰粫?huì)吧,我只是喜歡在幕后工作,并不是很愛(ài)拋頭露面而已!
developer:那你的主要工作內(nèi)容都是哪些呢?
runloop?。何野??主要是為了讓程序一直處于運(yùn)行狀態(tài),同時(shí)在運(yùn)行的狀態(tài)下處理一系列的事件,比如觸摸事件、定時(shí)器事件、selecter事件,在這樣的高強(qiáng)度的工作下還得維持良好的狀態(tài)!
developer:聽(tīng)起來(lái)很屌的樣子!跟我說(shuō)說(shuō)你跟線(xiàn)程的關(guān)系吧,聽(tīng)說(shuō)你兩是生死相依的兄弟!
runloop?。壕€(xiàn)程啊,我兩誰(shuí)也離不開(kāi)誰(shuí),他出生了我也就誕生了,當(dāng)他被銷(xiāo)毀的時(shí)候我也就撲街了。
developer:那平時(shí)我們拜訪線(xiàn)程挺容易的,要怎么去拜訪你呢?
runloop :有兩種方式
1、Foundation框架下的NSRunLoop
2、Core Foundation框架下得CFRunLoopRef
不論NSRunLoop和CFRunLoopRef都代表著我
[NSRunLoop currentRunLoop]; // 獲得當(dāng)前線(xiàn)程的RunLoop對(duì)象
[NSRunLoop mainRunLoop]; // 獲得主線(xiàn)程的RunLoop對(duì)象
CFRunLoopGetCurrent(); // 獲得當(dāng)前線(xiàn)程的RunLoop對(duì)象
CFRunLoopGetMain(); // 獲得主線(xiàn)程的RunLoop對(duì)象

看到這張圖沒(méi)?
這張圖就代表著一個(gè)完整的runloop,每一個(gè)runloop中都包含以下5個(gè)類(lèi):
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
如果沒(méi)有這5個(gè)類(lèi),runloop將會(huì)直接退出
developer:能詳細(xì)的介紹以下他們嗎?
runloop :當(dāng)然。
CFRunLoopModeRef
CFRunLoopModeRef 類(lèi)并沒(méi)有對(duì)外暴露,只是通過(guò) CFRunLoopRef 的接口進(jìn)行了封裝。
CFRunLoopModeRef代表RunLoop的運(yùn)行模式
一個(gè) RunLoop 包含若干個(gè) Mode,每個(gè) Mode 又包含若干個(gè) Source/Timer/Observer。每次調(diào)用 RunLoop 的主函數(shù)時(shí),只能指定其中一個(gè) Mode,這個(gè)Mode被稱(chēng)作 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個(gè) Mode 進(jìn)入。這樣做主要是為了分隔開(kāi)不同組的 Source/Timer/Observer,讓其互不影響。
系統(tǒng)默認(rèn)注冊(cè)了5個(gè)Mode:
kCFRunLoopDefaultMode:App的默認(rèn)Mode,通常主線(xiàn)程是在這個(gè)Mode下運(yùn)行
UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響
UIInitializationRunLoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用
GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
kCFRunLoopCommonModes: 這是一個(gè)占位用的Mode,不是一種真正的Mode
CFRunLoopSourceRef
CFRunLoopSourceRef 是事件產(chǎn)生的地方。Source有兩個(gè)版本:Source0 和 Source1。
? Source0 只包含了一個(gè)回調(diào)(函數(shù)指針),它并不能主動(dòng)觸發(fā)事件。使用時(shí),你需要先調(diào)用 CFRunLoopSourceSignal(source),將這個(gè) Source 標(biāo)記為待處理,然后手動(dòng)調(diào)用 CFRunLoopWakeUp(runloop) 來(lái)喚醒 RunLoop,讓其處理這個(gè)事件。
? Source1 包含了一個(gè) mach_port 和一個(gè)回調(diào)(函數(shù)指針),被用于通過(guò)內(nèi)核和其他線(xiàn)程相互發(fā)送消息。
CFRunLoopObserverRef
CFRunLoopObserverRef 是觀察者,每個(gè) Observer 都包含了一個(gè)回調(diào)(函數(shù)指針),當(dāng) RunLoop 的狀態(tài)發(fā)生變化時(shí),觀察者就能通過(guò)回調(diào)接受到這個(gè)變化??梢杂^測(cè)的時(shí)間點(diǎn)有以下幾個(gè):
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即將進(jìn)入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即將處理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即將處理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進(jìn)入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 剛從休眠中喚醒
kCFRunLoopExit = (1UL << 7), // 即將退出Loop
};
上面的 Source/Timer/Observer 被統(tǒng)稱(chēng)為 mode item,一個(gè) item 可以被同時(shí)加入多個(gè) mode。但一個(gè) item 被重復(fù)加入同一個(gè) mode 時(shí)是不會(huì)有效果的。如果一個(gè) mode 中一個(gè) item 都沒(méi)有,則 RunLoop 會(huì)直接退出,不進(jìn)入循環(huán)。
使用
- (void)observer{
// 創(chuàng)建observer CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"----監(jiān)聽(tīng)到RunLoop狀態(tài)發(fā)生改變---%zd", activity);
});
// 添加觀察者:監(jiān)聽(tīng)RunLoop的狀態(tài)
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
// 釋放Observer
CFRelease(observer);}
特別注意
/* CF的內(nèi)存管理(Core Foundation)
1.凡是帶有Create、Copy、Retain等字眼的函數(shù),創(chuàng)建出來(lái)的對(duì)象,都需要在最后做一次release * 比如CFRunLoopObserverCreate
2.release函數(shù):CFRelease(對(duì)象);
*/
CFRunLoopTimerRef
CFRunLoopTimerRef 是基于時(shí)間的觸發(fā)器,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一個(gè)時(shí)間長(zhǎng)度和一個(gè)回調(diào)(函數(shù)指針)。當(dāng)其加入到 RunLoop 時(shí),RunLoop會(huì)注冊(cè)對(duì)應(yīng)的時(shí)間點(diǎn),當(dāng)時(shí)間點(diǎn)到時(shí),RunLoop會(huì)被喚醒以執(zhí)行那個(gè)回調(diào)。
RunLoop 的內(nèi)部邏輯
根據(jù)蘋(píng)果在文檔里的說(shuō)明,RunLoop 內(nèi)部的邏輯大致如下:
可以看到,實(shí)際上 RunLoop 就是這樣一個(gè)函數(shù),其內(nèi)部是一個(gè) do-while 循環(huán)。當(dāng)你調(diào)用 CFRunLoopRun() 時(shí),線(xiàn)程就會(huì)一直停留在這個(gè)循環(huán)里;直到超時(shí)或被手動(dòng)停止,該函數(shù)才會(huì)返回。
developer:那我應(yīng)該怎么樣勞煩你去處理一些問(wèn)題呢?
runloop :解密-神秘的RunLoop這里有記錄怎么去使用runloop
developer:如何才能與你深交呢?
runloop :深入理解RunLoop
