Hi,runloop 交個朋友吧

眯大帥發表於2018-01-25

developer:hi,runloop 初次見面,交個朋友吧? runloop :你好,很高興認識你!

developer:我先自我介紹一下,我叫iOS Developer,你呢? runloop :我叫runloop,你的有些同伴叫我跑圈,我覺得叫我迴圈執行比較合適!

developer:我聽我的同伴說你有點不是很好相處啊,是不是真的? runloop :不會吧,我只是喜歡在幕後工作,並不是很愛拋頭露面而已!

developer:那你的主要工作內容都是哪些呢? runloop :我啊?主要是為了讓程式一直處於執行狀態,同時在執行的狀態下處理一系列的事件,比如觸控事件定時器事件selecter事件,在這樣的高強度的工作下還得維持良好的狀態!

developer:聽起來很屌的樣子!跟我說說你跟執行緒的關係吧,聽說你兩是生死相依的兄弟! runloop :執行緒啊,我兩誰也離不開誰,他出生了我也就誕生了,當他被銷燬的時候我也就撲街了。

developer:那平時我們拜訪執行緒挺容易的,要怎麼去拜訪你呢? runloop :有兩種方式 1、Foundation框架下的NSRunLoop 2、Core Foundation框架下得CFRunLoopRef 不論NSRunLoop和CFRunLoopRef都代表著我

[NSRunLoop currentRunLoop]; // 獲得當前執行緒的RunLoop物件
[NSRunLoop mainRunLoop]; // 獲得主執行緒的RunLoop物件
複製程式碼
CFRunLoopGetCurrent(); // 獲得當前執行緒的RunLoop物件
CFRunLoopGetMain(); // 獲得主執行緒的RunLoop物件
複製程式碼

相關類
看到這張圖沒? 這張圖就代表著一個完整的runloop,每一個runloop中都包含以下5個類: CFRunLoopRef CFRunLoopModeRef CFRunLoopSourceRef CFRunLoopTimerRef CFRunLoopObserverRef 如果沒有這5個類,runloop將會直接退出

developer:能詳細的介紹以下他們嗎? runloop :當然。

CFRunLoopModeRef CFRunLoopModeRef 類並沒有對外暴露,只是通過 CFRunLoopRef 的介面進行了封裝。 CFRunLoopModeRef代表RunLoop的執行模式

一個 RunLoop 包含若干個 Mode,每個 Mode 又包含若干個 Source/Timer/Observer。每次呼叫 RunLoop 的主函式時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個 Mode 進入。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響。

系統預設註冊了5個Mode: kCFRunLoopDefaultMode:App的預設Mode,通常主執行緒是在這個Mode下執行 UITrackingRunLoopMode:介面跟蹤 Mode,用於 ScrollView 追蹤觸控滑動,保證介面滑動時不受其他 Mode 影響 UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode,啟動完成後就不再使用 GSEventReceiveRunLoopMode: 接受系統事件的內部 Mode,通常用不到 kCFRunLoopCommonModes: 這是一個佔位用的Mode,不是一種真正的Mode

CFRunLoopSourceRef  CFRunLoopSourceRef 是事件產生的地方。Source有兩個版本:Source0 和 Source1。 • Source0 只包含了一個回撥(函式指標),它並不能主動觸發事件。使用時,你需要先呼叫 CFRunLoopSourceSignal(source),將這個 Source 標記為待處理,然後手動呼叫 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop,讓其處理這個事件。 • Source1 包含了一個 mach_port 和一個回撥(函式指標),被用於通過核心和其他執行緒相互傳送訊息。

CFRunLoopObserverRef CFRunLoopObserverRef 是觀察者,每個 Observer 都包含了一個回撥(函式指標),當 RunLoop 的狀態發生變化時,觀察者就能通過回撥接受到這個變化。可以觀測的時間點有以下幾個:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry         = (1UL << 0), // 即將進入Loop
    kCFRunLoopBeforeTimers  = (1UL << 1), // 即將處理 Timer
    kCFRunLoopBeforeSources = (1UL << 2), // 即將處理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進入休眠
    kCFRunLoopAfterWaiting  = (1UL << 6), // 剛從休眠中喚醒
    kCFRunLoopExit          = (1UL << 7), // 即將退出Loop
};
複製程式碼

上面的 Source/Timer/Observer 被統稱為 mode item,一個 item 可以被同時加入多個 mode。但一個 item 被重複加入同一個 mode 時是不會有效果的。如果一個 mode 中一個 item 都沒有,則 RunLoop 會直接退出,不進入迴圈。

使用

- (void)observer{ 
// 建立observer CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { 
NSLog(@"----監聽到RunLoop狀態發生改變---%zd", activity); 
}); 
// 新增觀察者:監聽RunLoop的狀態 
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); 
// 釋放Observer 
CFRelease(observer);}
複製程式碼

特別注意

/* CF的記憶體管理(Core Foundation) 
1.凡是帶有Create、Copy、Retain等字眼的函式,建立出來的物件,都需要在最後做一次release * 比如CFRunLoopObserverCreate 
2.release函式:CFRelease(物件); 
*/
複製程式碼

CFRunLoopTimerRef  CFRunLoopTimerRef 是基於時間的觸發器,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一個時間長度和一個回撥(函式指標)。當其加入到 RunLoop 時,RunLoop會註冊對應的時間點,當時間點到時,RunLoop會被喚醒以執行那個回撥。

RunLoop 的內部邏輯 根據蘋果在文件裡的說明,RunLoop 內部的邏輯大致如下:

RunLoop
可以看到,實際上 RunLoop 就是這樣一個函式,其內部是一個 do-while 迴圈。當你呼叫 CFRunLoopRun() 時,執行緒就會一直停留在這個迴圈裡;直到超時或被手動停止,該函式才會返回。

developer:那我應該怎麼樣勞煩你去處理一些問題呢? runloop :解密-神祕的RunLoop這裡有記錄怎麼去使用runloop

developer:如何才能與你深交呢? runloop :深入理解RunLoop

相關文章