面試題
- 講講 RunLoop,項目中有用到嗎?
- runloop內部實現邏輯?
- runloop和線程的關系?
- timer 與 runloop 的關系?
- 程序中添加每3秒響應一次的NSTimer,當拖動tableview時timer可能無法響應要怎么解決?
- runloop 是怎么響應用戶操作的, 具體流程是什么樣的?
- 說說runLoop的幾種狀態
- runloop的mode作用是什么?
簡介
什么是RunLoop
-
顧名思義,運行循環,在程序運行過程中循環做一些事情
- 應用范疇
- 定時器(Timer)、PerformSelector
- GCD Async Main Queue
- 事件響應、手勢識別、界面刷新
- 網絡請求
- AutoreleasePool
如果沒有RunLoop
執行完第13行代碼后,會即將退出程序
如果有了RunLoop
程序并不會馬上退出,而是保持運行狀態
RunLoop的基本作用
- 保持程序的持續運行
- 處理App中的各種事件(比如觸摸事件、定時器事件等)
- 節省CPU資源,提高程序性能:該做事時做事,該休息時休息
- ......
RunLoop對象
- iOS中有2套API來訪問和使用RunLoop
- Foundation:NSRunLoop
- Core Foundation:CFRunLoopRef
- NSRunLoop和CFRunLoopRef都代表著RunLoop對象
- NSRunLoop是基于CFRunLoopRef的一層OC包裝
- CFRunLoopRef是開源的 https://opensource.apple.com/tarballs/CF/
RunLoop與線程
- 每條線程都有唯一的一個與之對應的RunLoop對象
- RunLoop保存在一個全局的Dictionary里,線程作為key,RunLoop作為value
- 線程剛創建時并沒有RunLoop對象,RunLoop會在第一次獲取它時創建
- RunLoop會在線程結束時銷毀
- 主線程的RunLoop已經自動獲取(創建),子線程默認沒有開啟RunLoop
RunLoop對象
獲取RunLoop對象
- Foundation
- [NSRunLoop currentRunLoop]; // 獲得當前線程的RunLoop對象
- [NSRunLoop mainRunLoop]; // 獲得主線程的RunLoop對象
- Core Foundation
- CFRunLoopGetCurrent(); // 獲得當前線程的RunLoop對象
- CFRunLoopGetMain(); // 獲得主線程的RunLoop對象
Mode
RunLoop相關的類
Core Foundation中關于RunLoop的5個類
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
-
CFRunLoopObserverRef
CFRunLoopModeRef
- CFRunLoopModeRef代表RunLoop的運行模式
- 一個RunLoop包含若干個Mode,每個Mode又包含若干個Source0/Source1/Timer/Observer
- RunLoop啟動時只能選擇其中一個Mode,作為currentMode
- 如果需要切換Mode,只能退出當前Loop,再重新選擇一個Mode進入
- 不同組的Source0/Source1/Timer/Observer能分隔開來,互不影響
- 如果Mode里沒有任何Source0/Source1/Timer/Observer,RunLoop會立馬退出
- 常見的2種Mode
- kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默認Mode,通常主線程是在這個Mode下運行
- UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響
CFRunLoopObserverRef
添加Observer監聽RunLoop的所有狀態
RunLoop的運行邏輯
RunLoop的運行邏輯
-
Source0
- 觸摸事件處理
- performSelector:onThread:
-
Source1
- 基于Port的線程間通信
- 系統事件捕捉
-
Timers
- NSTimer
- performSelector:withObject:afterDelay:
-
Observers
- 用于監聽RunLoop的狀態
- UI刷新(BeforeWaiting)
- Autorelease pool(BeforeWaiting)
01、通知Observers:進入Loop
02、通知Observers:即將處理Timers
03、通知Observers:即將處理Sources
04、處理Blocks
05、處理Source0(可能會再次處理Blocks)
06、如果存在Source1,就跳轉到第8步
07、通知Observers:開始休眠(等待消息喚醒)
-
08、通知Observers:結束休眠(被某個消息喚醒)
- 01> 處理Timer
- 02> 處理GCD Async To Main Queue
- 03> 處理Source1
09、處理Blocks
10、根據前面的執行結果,決定如何操作
-01> 回到第02步
-02> 退出Loop11、通知Observers:退出Loop
RunLoop的運行邏輯
RunLoop休眠的實現原理
RunLoop休眠的實現原理
RunLoop在實際開中的應用
- 控制線程生命周期(線程保活)
- 解決NSTimer在滑動時停止工作的問題
- 監控應用卡頓
- 性能優化