Pre
- macOS:Catalina 10.15.7
- Xcode:12.3
- objc4:objc4-787.1
基本概念
CFRunloop
CFRunLoop
對象監視任務的輸入源,並在它們準備好進行處理時分派控制。
- 運行循環能夠監視三種類型的對象:
CFRunLoopSource
、CFRunLoopTimer
和CFRunLoopObserver
。
- 添加到運行循環中的每一個源、計時器和觀察者必須與一個或多個運行循環模式相關聯。
Core Foundation
定義了一種特殊的僞模式,稱爲common modes
,它容許您將多個模式與給定的source、timer或observer關聯起來。
- 每一個線程只有一個運行循環。你既不建立也不銷燬線程的運行循環。
Core Foundation
會根據須要自動爲您建立它。
- 運行循環能夠遞歸地運行。您能夠在任何運行循環調用中調用
CFRunLoopRun
或CFRunLoopRunInMode
,並在當前線程的調用堆棧上建立嵌套的運行循環激活。
Cocoa
應用程序構建在CFRunLoop
之上,實現它們本身的高級事件循環。在編寫應用程序時,能夠將源代碼、計時器和觀察者添加到它們的運行循環對象和模式中。而後,您的對象將做爲常規應用程序事件循環的一部分被監視。使用NSRunLoop
的gettcfrunloop方法能夠獲得對應的CFRunLoopRef
類型。
NSRunloop
NSRunLoop
是對Core Fundation
中的CFRunloop
的封裝
NSRunLoop
對象處理來自窗口系統的鼠標和鍵盤事件、NSPort
對象和NSConnection
對象等源的輸入。NSRunLoop
對象也會處理NSTimer事件。
- 你的應用程序既不建立也不顯式管理
NSRunLoop
對象。每一個NSThread
對象(包括應用程序的主線程)都有一個根據須要自動建立的NSRunLoop
對象。若是須要訪問當前線程的運行循環,可使用類方法currentRunLoop
來實現。
- 注意,從
NSRunLoop
的角度來看,NSTimer
對象不是「輸入」——它們是一種特殊的類型,這意味着當它們觸發時,不會致使運行循環返回。
NSRunLoop
類一般被認爲是線程不安全的,它的方法應該只在當前線程的上下文中被調用。永遠不要嘗試調用運行在不一樣線程中的NSRunLoop
對象的方法,由於這樣作可能會致使意想不到的結果。
線程 & Runloop
先看結論!劃重點!
- Cocoa和Core Foundation都提供了運行循環對象(
NSRunloop
和CFRunloop
)來幫助配置和管理線程的運行循環。
- 應用程序不須要顯式地建立這些對象;每一個線程(包括主線程)都有一個關聯的Runloop對象。
- 做爲應用程序啓動過程的一部分,應用程序框架自動在主線程上設置並運行運行循環。而子線程須要顯式地運行它們的運行循環。
- 關係歸納:App啓動後,蘋果在主線程建立了其關聯的Runloop,並在該
Runloop
中註冊兩個Observer
- 第一個
Observer
(優先級最高)監控的事件:Entry(即將進入Loop
),回調內會調用_objc_autoreleasePoolPush()
建立自動釋放池
- 第二個
Observer
(優先級最低)監控兩個事件:
- 第一個事件:BeforeWaiting(準備進入休眠),回調內會調用
_objc_autoreleasePoolPop()
釋放舊的池並調用_objc_autoreleasePoolPush()
建立新的池
- 第二個事件:Exit(即將退出
Loop
),回調內會調用_objc_autoreleasePoolPop()
銷燬自動釋放池
關係分析
- 根據調用堆棧咱們能夠看出來,當調用
UIApplicationMain()
方法後,系統會自動爲其建立相關聯的Runloop
。
- 經過在main()函數首行即獲取主線程與主runloop能夠看到:此時主線程的runloop已經存在了。咱們能夠推測出主線程建立後即會建立對應的runloop,也就是說,主runloop在程序一啓動就默認建立好了。 可是此時的主runloop中,還未添加相關觀察者等等。
- 當代碼從main()開始執行,此時的runloop依然是一個空的結構體
- 查看進入
applicationDidLaunchingWithOptions:
以前的調用堆棧 能夠看到會爲此時的主Runloop添加Source等相關信息
- 走進
applicationDidLaunchingWithOptions
時,再經過po CFRunloopGetMain()
獲取此時的主Runloop信息,能夠看到觀察者、source等都已添加完成
// 截取部分
current mode = kCFRunLoopDefaultMode,
common modes = <CFBasicHash 0x6000014e5260 [0x7fff8002e8c0]>{type = mutable set, count = 2,
entries =>
0 : <CFString 0x7fff806610e0 [0x7fff8002e8c0]>{contents = "UITrackingRunLoopMode"}
2 : <CFString 0x7fff801ab7e8 [0x7fff8002e8c0]>{contents = "kCFRunLoopDefaultMode"}
}