iOS开发 - Runloop的mode是如何切换的

问题

  1. Runloop的mode是如何切换的?
  2. Runloop的mode切换时,上一个mode是需要退出吗?

探索

Runloop的mode是如何切换的

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

mode切换时会退出吗?

通过监听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

©️2020 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值