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