概述
我們的應(yīng)用不能對 NSRunLoop
對象進(jìn)行創(chuàng)建和直接的管理操作。每一個 NSThread
對象(包括 main thread)都按需自動創(chuàng)建一個 NSRunLoop 對像(這里的意思是:線程剛創(chuàng)建時并沒有 RunLoop,如果你不主動獲取,那它一直都不會有)。通過 currentRunLoop()
方法可以訪問當(dāng)前線程的 run loop。
NSRunLoop 和 CFRunLoopRef
NSRunLoop
是基于 CFRunLoopRef 的封裝,提供了面向?qū)ο蟮?API,但是這些 API 不是線程安全 (thread-safe)的。CFRunLoopRef
是在 CoreFoundation 框架內(nèi)的,提供了純 C 函數(shù)的 API,所有這些 API 都是線程安全的。
RunLoop 的構(gòu)成
Thread包含一個CFRunLoop,一個CFRunLoop包含一種 CFRunLoopMode,mode包含CFRunLoopSource,CFRunLoopTimer和CFRunLoopObserver。
CFRunLoopModeRef
NSDefaultRunLoopMode: 默認(rèn),空閑狀態(tài)
UITrackingRunLoopMode: ScrollView滑動時
UIInitializationRunLoopMode: 在剛啟動App時第進(jìn)入的第一個 Mode,啟動完成后就退出
GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的 Mode
NSRunLoopCommonModes: 占位用的Mode
每次調(diào)用 RunLoop 的主函數(shù)時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個 Mode 進(jìn)入。
CFRunLoopSourceRef
事件產(chǎn)生的地方,CFRunLoopSourceRef
有兩個版本:Source0 和 Source1。
Source0 只包含了一個回調(diào)(函數(shù)指針),它并不能主動觸發(fā)事件。使用時,你需要先調(diào)用 CFRunLoopSourceSignal(source),將這個 Source 標(biāo)記為待處理,然后手動調(diào)用 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop,讓其處理這個事件。
Source1 包含了一個 mach_port 和一個回調(diào)(函數(shù)指針),被用于通過內(nèi)核和其他線程相互發(fā)送消息。這種 Source 能主動喚醒 RunLoop 的線程。
CFRunLoopTimerRef
它和 NSTimer 是 toll-free bridged
的,可以混用。其包含一個時間長度和一個回調(diào)(函數(shù)指針)。RunLoop為了節(jié)省資源,并不會在非常準(zhǔn)確的時間點回調(diào)Timer。Timer 有個屬性叫做 Tolerance (寬容度),標(biāo)示了當(dāng)時間點到后,容許有多少最大誤差。
CFRunLoopObserverRef
每個 Observer 都包含了一個回調(diào)(函數(shù)指針),當(dāng) RunLoop 的狀態(tài)發(fā)生變化時,觀察者就能通過回調(diào)接受到這個變化。可以觀測的時間點有以下幾個:
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
};
參考:
http://blog.ibireme.com/2015/05/18/runloop/