美文网首页
runloop底层源码探究二(runloop线程保活)

runloop底层源码探究二(runloop线程保活)

作者: iOS劝退师 | 来源:发表于2021-01-11 15:35 被阅读0次

在main函数中调用CFRunLoopRun();之后的测试代码还会打印吗?

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }

    CFRunLoopRun();
    NSLog(@"test CFRunLoopRun");

    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

答案是不会,代码会一直在runloop的do while循环中等待,无法执行到测试的打印代码。这就是线程保活的关键
分析下源码:

void CFRunLoopRun(void) {   /* DOES CALLOUT */
    int32_t result;
    do {

        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

可以看到在runloop没有停止过结束的情况下,runloop一直在do while循环

do while内部逻辑(看注释):
(......处删掉了部分代码,为了看起来逻辑简洁)

//CFRunLoopRunSpecific简化后的代码
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    ......
    //获取当前mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
//判断当前mode是否为空(代码见下一段)
//判断条件:是否存在sources0,sources1,timers其中一种(如果都为空,runloop就会退出循环,主线程不判断sources和timers,具体原因看下一段代码的注释)
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
        Boolean did = false;
        ......
        return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    ......
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
     ......
    return result;
}
// expects rl and rlm locked
static Boolean __CFRunLoopModeIsEmpty(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopModeRef previousMode) {
 CHECK_FOR_FORK();
    if (NULL == rlm) return true;
#if TARGET_OS_WIN32
    if (0 != rlm->_msgQMask) return false;
#endif
#if __HAS_DISPATCH__
//这里判断是否是主线程,如果是主线程,就直接返回 了false,不进行下面的判断,这就是主线程和其他线程的判断区别(因此主线程的runloop不会退出)
    Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
    if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) return false; // represents the libdispatch main queue
#endif
    if (NULL == rlm) return true;

    if (NULL != rlm->_sources0 && 0 < CFSetGetCount(rlm->_sources0)) return false;
    if (NULL != rlm->_sources1 && 0 < CFSetGetCount(rlm->_sources1)) return false;
    if (NULL != rlm->_timers && 0 < CFArrayGetCount(rlm->_timers)) return false;
    ......
    return true;
}

相关文章

网友评论

      本文标题:runloop底层源码探究二(runloop线程保活)

      本文链接:https://www.haomeiwen.com/subject/hyjfaktx.html