RunLoop
- 保持程序點持續(xù)運行
- 處理App的各種事件
- 節(jié)省CPU資源(該做事的時候做事,該休息的時候休息) 提高程序性能.實現(xiàn)省電,流暢,響應速度快,用戶體驗好。
RunLoop定義
當有持續(xù)的異步任務需求時,我們會創(chuàng)建一個獨立的生命周期可控的線程.RunLoop就是控制線程生命周期并接收事件進行處理的機制。RunLoop是iOS事件響應與任務處理最核心的機制,它貫穿了iOS整個系統(tǒng)。 運用到RunLoop的框架是 Foundation 和 CoreFoundation C語言框架
RunLoop內(nèi)部實現(xiàn)原理
- 1.有一個判斷條件,滿足條件就無限循環(huán)(其實就是一個 do { ... } while函數(shù))
- 2.線程得到喚醒事件被喚醒,事件處理完畢之后,回到睡眠狀態(tài),等待下次喚醒。
RunLoop特性
- 主線程的RunLoop在應用啟動的時候就會創(chuàng)建
- 其它線程則需要在該線程下手動啟動
- 不能自己創(chuàng)建RunLoop對象
- RunLoop并不是線程安全的,所以需要避免在其它線程上調(diào)用當前線程的RunLoop
- RunLoop負責管理autorelease pools
- RunLoop負責各類處理消息事件(例如觸摸事件、點擊事件等等
RunLoop與線程的關(guān)系
- RunLoop是用來管理線程的,每條線程都有唯一一個與之對應的RunLoop對象
- 當線程的RunLoop開啟后,線程就會在執(zhí)行完任務后,處于休眠狀態(tài),隨時等待接受新的任務,而不是退出。只有主線程的RunLoop是默認開啟的,所以程序在開啟后會一直運行,不會退出。其它線程的RunLoop如果需要開啟,則手動開啟。
- 線程在執(zhí)行中的休眠和激活就是由RunLoop對象來進行管理的。
RunLoop五種模式
- NSDefauleRunLoopMode(默認模式)
- UITrackingRunLoopMode(只在拖拽的時候觸發(fā)的模式): 定時器會在 UITrackingRunLoopMode 模式下工作.一旦進入其他模式,定時器就不會被執(zhí)行.RunLoop只能執(zhí)行一種模式,拖拽使用時的Mode
- NSRunLoopCommonModes(通用模式): 標記為Commen Modes的模式,UITrackingRunLoopMode NSDefaultRnuLoopMode 在這兩種模式下都可以運行
- UIInitializationRunLoopMode(初始RunLoop):在剛啟動App時進入的第一個Mode,啟動完成后就用不到了。
- GSEventReceiveRunLoopMode(事件接收): 接受系統(tǒng)事件的內(nèi)部Mode,通常用不到。
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
RunLoop處理的模式的三大塊內(nèi)容: Source 、 Observer、Timer
1.Timer:定時器
-
2.Source:輸入源 / 事件源. 分類:
- port-based Source,與其它線程進行交互,蘋果內(nèi)部的Source
- Custom Input Source:自定義輸入源
- Cocoa Perform Selector Source:處理 Perform afterDelay...事件源.
按照函數(shù)調(diào)用棧,Source 的 分類: - 1.非基于Port的
- 2.基于Port的,通過內(nèi)核和其它線程通信,接收,分發(fā)系統(tǒng)事件。
-
3.Observer:觀察者.
// 添加RunLoop觀察者,監(jiān)聽RunLoop狀態(tài), CFRunLoopAddObserver(<#CFRunLoopRef rl#>, <#CFRunLoopObserverRef observer#>, <#CFRunLoopMode mode#>)基本用法:
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopEntry, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { NSLog(@"監(jiān)聽到RunLoop發(fā)生改變"); }); // 添加觀察者 CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); // 釋放觀察者 凡是C語言通過retain/create/copy/創(chuàng)建出來的對象記得要釋放 CFRelease(observer); // <#CFAllocatorRef allocator#>:默認值 CFAllocatorGetDefault() // <#CFOptionFlags activities#>:要監(jiān)聽哪些活動狀態(tài) /* typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { kCFRunLoopEntry, // 1 進入RunLoop循環(huán) kCFRunLoopBeforeTimers, // 2 處理定時前回調(diào) kCFRunLoopBeforeSources, // 4 處理輸入源/事件源的事件事件 kCFRunLoopBeforeWaiting, // 32 RunLoop睡眠前調(diào)用 kCFRunLoopAfterWaiting, // 64 runloop喚醒后調(diào)用 kCFRunLoopExit, // 128 退出RunLoop kCFRunLoopAllActivities }; */ // <#Boolean repeats#>:是否重復,默認為YES // <#CFIndex order#>:默認為0
整體處理邏輯
1.通知Observer,即將進入RunLoop (Observer) --> 2.通知Observer將要處理Timer (Observer) --> 3.通知Observer,將要處理Source() (Observer) --> 4.處理Source0 --> 5.如果有Source1,跳到第九9步 --> 6.通知Observer,線程即將進入睡眠狀態(tài) --> 7.休眠,等待喚醒 --> 8.通知Observer,線程剛被喚醒 --> 9.處理喚醒時收到的消息,之后跳回2. --> 10.通知Observer,RunLoop即將退出
RunLoop應用實踐
- 維護線程的生命周期,讓線程不會自動退出,通過變量手動設置啟動/退出
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
while (!self.isCancelled && !self.isFinished) {
@autoreleasepool {
[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
}
}
- 創(chuàng)建常駐線程,執(zhí)行一些會一直存在的任務。該線程的生命周期與App的主線程生命周期一樣
@autoreleasepool {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
- 在一定時間內(nèi)監(jiān)聽某中事件,或者執(zhí)行某種任務的線程
// 在30分鐘內(nèi),每隔30秒執(zhí)行onTimerFired:。這種場景一般出現(xiàn)在: 我需要在App啟動之后,在一定時間內(nèi)持續(xù)更新某項數(shù)據(jù)
@autoreleasepool {
NSRunLoop * runLoop = [NSRunLoop currentRunLoop];
NSTimer * udpateTimer = [NSTimer timerWithTimeInterval:30
target:self
selector:@selector(onTimerFired:)
userInfo:nil
repeats:YES];
[runLoop addTimer:udpateTimer forMode:NSRunLoopCommonModes];
[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:60*30]];
}