轉載自原文,參入自己的了解。
另外有一篇比較好介紹RunLoop與NSTimer之間的關系的文章,鏈接在這
RunLoop解決什么問題
手機是事件驅動的架構。一般來講,一個線程一次只能執行一個任務,執行完成后線程就會退出。如果我們需要一個機制,讓線程能隨時處理事件但并不退出
具體來說:
1.程序一直運行并接受用戶輸入。
2.決定程序何時應該處理哪些Event。
3.調節解耦 被調方和主調方解耦
4.節省CPU時間
在Cocoa中哪些用到了RunLoop
1.NSTimer
2.UIEvent
3.AutoRelease
4.NSObject(NSDelayPerforming) NSObject (NSThreadPerformAddtion)
5.CA層接口
6.GCD Dispatch_get_main_queue()
7.NSURLConnection AFNetworking
Event Loop 在很多系統和框架里都有實現,比如 Node.js 的事件處理,比如 Windows 程序的消息循環,再比如 OSX/iOS 里的 RunLoop。實現這種模型的關鍵點在于:如何管理事件/消息,如何讓線程在沒有處理消息時休眠以避免資源占用、在有消息到來時立刻被喚醒。
線程和 RunLoop 之間是一一對應的,其關系是保存在一個全局的 Dictionary 里。線程剛創建時并沒有 RunLoop,如果你不主動獲取,那它一直都不會有。RunLoop 的創建是發生在第一次獲取時,RunLoop 的銷毀是發生在線程結束時。你只能在一個線程的內部獲取其 RunLoop(主線程除外)
但是并不是說一個Thread只能起一個RunLoop,可以起很多,但是必須是嵌套的,就是一個RunLoop,里面可以再嵌套RunLoop,但是根RunLoop只有一個。
RunLoopMode是RunLoop的核心,跑RunLoop必須在固定的模式下。
CFRunLoopSource,CFRunLoopTimer,CFRunLoopObserver.
1.CFRunLoopTimer NSTimer是對CFRunLoopTimer的封裝。
2.CFRunLoopSource 比較抽象的一個概念,形象講就是數據源的抽象類。有兩個version的source:source0,source1.
source0:處理app內部事件,app自己負責管理(觸發)。如UIEvent,CFSocket。
source1:由RunLoop和系統內核管理,mach port驅動,如CFPort,CFMessagePort。
如有需要,自己選擇一種source,來實現。
CFRunLoopObserver:向外部報告RunLoop的狀態的改變,框架中很多機制都由RunLoopOberserver觸發,如CAAnimation。
RunLoop 對外的接口
在 CoreFoundation 里面關于 RunLoop 有5個類:
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
其中 CFRunLoopModeRef 類并沒有對外暴露,只是通過 CFRunLoopRef 的接口進行了封裝。一個 RunLoop 包含若干個 Mode,每個 Mode 又包含若干個 Source/Timer/Observer。每次調用 RunLoop 的主函數時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個 Mode 進入。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響。
**CFRunLoopSourceRef **是事件產生的地方。Source有兩個版本:Source0 和 Source1。
- Source0 只包含了一個回調(函數指針),它并不能主動觸發事件。使用時,你需要先調用 CFRunLoopSourceSignal(source),將這個 Source 標記為待處理,然后手動調用 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop,讓其處理這個事件。
- Source1 包含了一個 mach_port 和一個回調(函數指針),被用于通過內核和其他線程相互發送消息。這種 Source 能主動喚醒 RunLoop 的線程,其原理在下面會講到。
CFRunLoopTimerRef 是基于時間的觸發器,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一個時間長度和一個回調(函數指針)。當其加入到 RunLoop 時,RunLoop會注冊對應的時間點,當時間點到時,RunLoop會被喚醒以執行那個回調。
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
};
RunLoopObserver 與 AutoReasePool
- UIKit通過RunLoopObserver在RunLoop兩次Sleep之間對AutoReleasePool進行Pop和Push,將這次Loop產生的內存池進行釋放。
Mode是IOS滑動技術的關鍵蘋果公開提供的 Mode 有兩個:kCFRunLoopDefaultMode (NSDefaultRunLoopMode) 和 UITrackingRunLoopMode,你可以用這兩個 Mode Name 來操作其對應的 Mode。
CFRunLoopMode
- RunLoop在同一時間必須只能在一種Mode下run
- RunLoop如果想更換Mode,則必須停止當前Loop