iOS-RunLoop
RunLoop總結
需要思考問題
為什么程序在使用的時候可以接受用戶的觸摸事件,不使用的時候什么事件都不發生?
查看一下 main.m 文件,這是程序啟動的入口函數。
int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }
代碼調用的時候,主線程也開始運行。文檔上說 UIApplicationMain 函數指定了appliacaion的delegate,并且開啟了event cycle。那什么是event cycle?
RunLoop
程序的event cycle其實就是RunLoop,RunLoop在程序啟動的時候就開啟,接受用戶的處理事件。
這是官方文檔上面的一個圖,可以看到RunLoop在線程中循環監聽需要處理的事件。能夠接收兩種不同的事件源
- Input sources(輸入源):傳遞異步事件。
- Timer sources(定時器): 傳遞同步事件、發生在特定時間或者重復的時間間隔。
RunLoop Mode
-
Default模式:幾乎包含了所有輸入源,一般情況下使用這個模式
- NSDefaultRunLoopMode (Cocoa)
- kCFRunLoopDefaultMode (Core Foundation)
-
Connection模式
- 處理NSConnection對象相關事件,系統內部使用,用戶基本不會使用。
- Modal模式
- 處理modal panels事件
-
Event tracking模式:滑動Scrollview、Tableview的時候就處于這個模式
- UITrackingRunLoopMode(iOS)
- NSEventTrackingRunLoopMode(cocoa)
-
Common模式: 模式的組合,所有的模式下都可以處理
- NSRunLoopCommonModes (Cocoa)
- kCFRunLoopCommonModes (Core Foundation)
RunLoop與線程的關系
每條線程都有唯一的RunLoop對象與之對應,主線程的RunLoop是自動創建并啟動的。子線程的RunLoop需要手動創建,并且調用 run 方法啟動。 currentRunLoop 是延遲加載的,只創建一次.
[[NSRunLoop currentRunLoop] run];
指定RunLoop運行的模式和過期時間
- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;
使用場景
1、NSTimer
如果在ScrollView里面使用到NSTimer的,需要調用 addTimer:forMode 方法指定model,才能在滾動的時候生效。
NSTimer創建的定時器因為需要指定model運行,在RunLoop中需要處理各種事件,導致NSTimer不精確。可以使用GCD方法來創建,GCD不受RunLoop的Mode影響.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); // 要搞一個強引用,不然定時器創建出來就掛了 self.timer = timer; // 延遲3秒開始執行 dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)); // 每隔2秒執行一次 uint64_t interval = (uint64_t)(2.0 * NSEC_PER_SEC); dispatch_source_set_timer(self.timer, start, interval, 0); // 要執行的任務 dispatch_source_set_event_handler(self.timer, ^{ NSLog(@"%s",__func__); }); // 啟動定時器 dispatch_resume(self.timer); // 停止定時器 dispatch_cancel(self.timer);
2、ImageView的加載
圖片的加載放在 NSDefaultRunLoopMode ,因為ScrollView的滾動是在 UITrackingRunLoopMode 下運行的。
3、PerformSelector指定執行的模式
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSString *> *)modes;
參考
1、 官方文檔