UITableView滑动时,Runloop会进行切换mode,由kCFRunLoopDefaultMode
切换为UITrackingRunLoopMode
,根据源码,切换mode实际是调用CFRunLoopRunSpecific
这个函数
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
__CFRunLoopLock(rl);
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
Boolean did = false;
if (currentMode) __CFRunLoopModeUnlock(currentMode);
__CFRunLoopUnlock(rl);
return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
}
volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
CFRunLoopModeRef previousMode = rl->_currentMode;
rl->_currentMode = currentMode;
int32_t result = kCFRunLoopRunFinished;
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
__CFRunLoopModeUnlock(currentMode);
__CFRunLoopPopPerRunData(rl, previousPerRun);
rl->_currentMode = previousMode;
__CFRunLoopUnlock(rl);
return result;
}
使用 lldb
来断点这个函数,当滑动 UITableView
时
* thread #6, name = 'com.apple.uikit.eventfetch-thread', stop reason = breakpoint 4.1
* frame #0: 0x00007fff23d9a7b0 CoreFoundation`CFRunLoopRunSpecific
frame #1: 0x00007fff25939c71 Foundation`-[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 211
frame #2: 0x00007fff25939ee0 Foundation`-[NSRunLoop(NSRunLoop) runUntilDate:] + 72
frame #3: 0x00007fff48d39bfb UIKitCore`-[UIEventFetcher threadMain] + 138
frame #4: 0x00007fff2594f9eb Foundation`__NSThread__start__ + 1047
frame #5: 0x00007fff51c0c109 libsystem_pthread.dylib`_pthread_start + 148
frame #6: 0x00007fff51c07b8b libsystem_pthread.dylib`thread_start + 15
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
* frame #0: 0x00007fff23d9a7b0 CoreFoundation`CFRunLoopRunSpecific
frame #1: 0x00007fff38ba6c1a GraphicsServices`GSEventRunModal + 139
frame #2: 0x00007fff48c8b9ec UIKitCore`UIApplicationMain + 1605
frame #3: 0x000000010506f8fa TestIsland`main(argc=1, argv=0x00007ffeeab8fd00) at main.m:18:12
frame #4: 0x00007fff51a231fd libdyld.dylib`start + 1
通过监听runloop回调,来判断mode切换时,是否会退出当前mode的循环,然后开启一个新循环
static CFRunLoopObserverRef observer;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFOptionFlags activities = (kCFRunLoopEntry|kCFRunLoopBeforeWaiting | // before the run loop starts sleeping
kCFRunLoopExit); // before exiting a runloop run
observer = CFRunLoopObserverCreateWithHandler(NULL, // allocator
activities, // activities
YES, // repeats
INT_MAX, // order after CA transaction commits
^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(rl);
NSString *modestr = (__bridge NSString *)mode;
if (activity == kCFRunLoopEntry) {
printf("entry current mode is %s\n",modestr.UTF8String);
}else if (activity == kCFRunLoopBeforeWaiting) {
printf("beforewait current mode is %s\n",modestr.UTF8String);
}else if (activity == kCFRunLoopExit){
printf("exit current mode is %s\n",modestr.UTF8String);
}
});
CFRunLoopAddObserver(runLoop, observer, kCFRunLoopCommonModes);
CFRelease(observer);
最终日志如下:
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
exit current mode is kCFRunLoopDefaultMode
entry current mode is UITrackingRunLoopMode
beforewait current mode is UITrackingRunLoopMode
beforewait current mode is UITrackingRunLoopMode
beforewait current mode is UITrackingRunLoopMode
...
beforewait current mode is UITrackingRunLoopMode
exit current mode is UITrackingRunLoopMode
entry current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
beforewait current mode is kCFRunLoopDefaultMode
说明切换时,会先退出 kCFRunLoopDefaultMode 的runloop循环,再重新进入 UITrackingRunLoopMode 的runloop循环。
demo 链接: https://pan.baidu.com/s/1J0L7H1AbfRy–1Jjx7nEgg 密码: 1cs9