Run Loopgit
運行循環github
Run Loop 兩大功能:數組
從睡眠中 -> 處理消息, 須要一個喚醒的過程數據結構
一、 講講 RunLoop, 項目中有用到嗎?app
appDelegate, 是一個 RunLoop 對象ide
RunLoop 的基本做用:oop
保持程序的持續運行性能
節省 CPU 的資源,提升程序的性能 ( 沒有事情,就請休眠,不要功耗。有事情,就處理)ui
二、 RunLoop 內部實現邏輯?this
RunLoop 裏面有不少種模式,他在運行的過程當中,只會選擇一種來運行
Modes, 就是 RunLoop 平時要作的事情
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
CFRunLoopModeRef
不一樣組的 Source0 / Source1 / Timer / Observer 能分割開來,互不影響
__CFRunLoop
的數據結構struct __CFRunLoop { CFRuntimeBase _base; pthread_mutex_t _lock; /* locked for accessing mode list */ __CFPort _wakeUpPort; // used for CFRunLoopWakeUp Boolean _unused; volatile _per_run_data *_perRunData; // reset for runs of the run loop pthread_t _pthread; uint32_t _winthread; CFMutableSetRef _commonModes; CFMutableSetRef _commonModeItems; CFRunLoopModeRef _currentMode; // 這裏有一個集合 // 裝的是一些 CFRunLoopModeRef CFMutableSetRef _modes; // _modes 裏面有不少 mode ( CFRunLoopModeRef ) // 其中有一個 mode 是 _currentMode ( 當前模式 ) struct _block_item *_blocks_head; struct _block_item *_blocks_tail; CFAbsoluteTime _runTime; CFAbsoluteTime _sleepTime; CFTypeRef _counterpart; };
進入 mode
CFRunLoopModeRef 的數據結構
typedef struct __CFRunLoopMode *CFRunLoopModeRef; struct __CFRunLoopMode { CFRuntimeBase _base; pthread_mutex_t _lock; /* must have the run loop locked before locking this */ CFStringRef _name; Boolean _stopped; char _padding[3]; // 這兩個集合,裝的是 CFRunLoopSourceRef 對象 CFMutableSetRef _sources0; CFMutableSetRef _sources1; // 對應平時的事情: // 包括點擊事件,刷新 UI 事件,performSelector // 這個數組,裝的是 CFRunLoopObserverRef 對象 CFMutableArrayRef _observers; // 監聽器 // 這個數組,裝的是 CFRunLoopTimerRef 對象 CFMutableArrayRef _timers; // 對應計時器, 定時器 CFMutableDictionaryRef _portToV1SourceMap; __CFPortSet _portSet; CFIndex _observerMask; #if USE_DISPATCH_SOURCE_FOR_TIMERS dispatch_source_t _timerSource; dispatch_queue_t _queue; Boolean _timerFired; // set to true by the source when a timer has fired Boolean _dispatchTimerArmed; #endif #if USE_MK_TIMER_TOO mach_port_t _timerPort; Boolean _mkTimerArmed; #endif #if DEPLOYMENT_TARGET_WINDOWS DWORD _msgQMask; void (*_msgPump)(void); #endif uint64_t _timerSoftDeadline; /* TSR */ uint64_t _timerHardDeadline; /* TSR */ };
三、 RunLoop 和線程的關係?
runloops[thread] = runloop
線程都沒有了,runloop 也就沒有意義了
子線程,要什麼 runloop?沒有 runloop , 就是命令行,調用一次就完結
有了 runloop, 能夠反覆休眠、喚醒、處理消息
子線程中,獲取一下 currentRunLoop, 就建立開啓了 RunLoop
CFRunLoopRef CFRunLoopGetCurrent(void) { CHECK_FOR_FORK(); CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop); if (rl) return rl; // 從這裏拿 return _CFRunLoopGet0(pthread_self()); }
進入詳情
// should only be called by Foundation // t==0 is a synonym for "main thread" that always works CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) { if (pthread_equal(t, kNilPthreadT)) { t = pthread_main_thread_np(); } __CFLock(&loopsLock); if (!__CFRunLoops) { __CFUnlock(&loopsLock); CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np()); CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop); if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) { CFRelease(dict); } CFRelease(mainLoop); __CFLock(&loopsLock); } CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); __CFUnlock(&loopsLock); if (!loop) { CFRunLoopRef newLoop = __CFRunLoopCreate(t); __CFLock(&loopsLock); // 從字典裏面,獲取 runloop 對象 // __CFRunLoops 字典 // pthreadPointer(t) ,鍵 key loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); // runloop 對象 loop 不存在,就新建 if (!loop) { // 字典中,設置 // __CFRunLoops 字典 // pthreadPointer(t),鍵 key // newLoop, 值 value CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop); loop = newLoop; } // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it __CFUnlock(&loopsLock); CFRelease(newLoop); } if (pthread_equal(t, pthread_self())) { _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL); if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) { _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop); } } return loop; }
線程之間通訊 (交互),通常經過端口 port 的形式
四、 timer 和 runloop 的關係?
五、 程序中添加每 3 秒響應一次的 NSTimer, 當拖動 tableView 時 timer 可能沒法響應,要怎麼解決?
CFRunLoopModeRef
常見的 2 種 Mode:
切換 Mode:
是在 while 的循環中進行,至關於一次 goto, 或者 continue
六、 RunLoop 是怎麼響應用戶操做的,具體流程是什麼樣的?
七、 說說 RunLoop 的幾種狀態
切換 RunLoop 的模式,須要 RunLoop 退出和從新進入
/* Run Loop Observer Activities */ typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { kCFRunLoopEntry = (1UL << 0), // 即將進入 Loop kCFRunLoopBeforeTimers = (1UL << 1), // 即將處理 Timer kCFRunLoopBeforeSources = (1UL << 2), // 即將處理 Source kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進入休眠 kCFRunLoopAfterWaiting = (1UL << 6), // 剛從休眠中喚醒 // 喚醒 kCFRunLoopExit = (1UL << 7), // 即將退出 Loop kCFRunLoopAllActivities = 0x0FFFFFFFU };
RunLoop 的 Before 什麼,就是即將處理什麼
八、 runloop 的 mode 做用是什麼?
source 0
處理觸摸 ( 點擊 )事件
performSelector: onThread: (把方法放在子線程,去執行)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ NSLog(@"%s", __func__); // 斷點, bt 一下 }
點擊一下,進入斷點
(lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 // 咱們的 source code * frame #0: 0x0000000104d2eed4 RunLooper`-[ViewController touchesBegan:withEvent:](self=0x00007fe015509870, _cmd="touchesBegan:withEvent:", touches=1 element, event=0x00006000016998c0) at ViewController.m:43:1 frame #1: 0x00007fff48cb94e2 UIKitCore`forwardTouchMethod + 323 frame #2: 0x00007fff48cb938e UIKitCore`-[UIResponder touchesBegan:withEvent:] + 49 frame #3: 0x00007fff48cc82ab UIKitCore`-[UIWindow _sendTouchesForEvent:] + 622 frame #4: 0x00007fff48cca311 UIKitCore`-[UIWindow sendEvent:] + 4501 frame #5: 0x00007fff48ca4755 UIKitCore`-[UIApplication sendEvent:] + 356 frame #6: 0x00007fff48d2f552 UIKitCore`__dispatchPreprocessedEventFromEventQueue + 7628 frame #7: 0x00007fff48d32716 UIKitCore`__handleEventQueueInternal + 6584 // 處理事件 frame #8: 0x00007fff48d28fb9 UIKitCore`__handleHIDEventFetcherDrain + 88 // source 0 frame #9: 0x00007fff23da0d31 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17 frame #10: 0x00007fff23da0c5c CoreFoundation`__CFRunLoopDoSource0 + 76 // source 0 frame #11: 0x00007fff23da0434 CoreFoundation`__CFRunLoopDoSources0 + 180 frame #12: 0x00007fff23d9b02e CoreFoundation`__CFRunLoopRun + 974 frame #13: 0x00007fff23d9a944 CoreFoundation`CFRunLoopRunSpecific + 404 frame #14: 0x00007fff38ba6c1a GraphicsServices`GSEventRunModal + 139 frame #15: 0x00007fff48c8b9ec UIKitCore`UIApplicationMain + 1605 frame #16: 0x0000000104d2f182 RunLooper`main(argc=1, argv=0x00007ffeeaecfd30) at main.m:18:12 frame #17: 0x00007fff51a231fd libdyld.dylib`start + 1
source 1
(例如: 在屏幕上點一下,產生一個點擊事件,
這個點擊事件,一開始,屬於 source 1,
再包裝成 source 0 ,來處理 )
屏幕點擊, source 1 捕捉,分發到 source 0 來處理
Timers
Observers
監聽器一旦監聽到, runLoop 要休眠了,
他就會在 runLoop 休眠以前,
刷新 UI 界面
runLoop 休眠以前, 清理一下自動釋放池
使用的代碼, 目前最新 1153