美文网首页
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