Runloop源码分析(4)——addTimer

2021-01-21  本文已影响0人  无悔zero

先看看Runloop最常见的应用,滑动页面时定时器会停止,所以要处理:

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
    NSLog(@"1");
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode: NSRunLoopCommonModes];

原因:

RunLoopMode可以切换,默认为kCFRunLoopDefaultMode,滑动时为UITrackingRunLoopMode,启动时为UIInitializationRunLoopModetimer加入的RunLoopMode默认是kCFRunLoopDefaultMode,当页面滑动时,RunLoopMode自动切换到UITrackingRunLoopMode,因此timer失效。当停止滑动时,RunLoopMode又切换回kCFRunLoopDefaultModetimer恢复。

  1. 我们从源码看看做了什么:
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {    
    ...
    if (modeName == kCFRunLoopCommonModes) {
        CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;//<CFSetRef>{defaultMode, TrackingMode}
        if (NULL == rl->_commonModeItems) {
            rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);//懒加载
        }
        CFSetAddValue(rl->_commonModeItems, rlt);//添加timer
        if (NULL != set) {
            CFTypeRef context[2] = {rl, rlt};
            CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);//遍历_commonModes,将 timer 添加到 commonModes 的所有模式下
            CFRelease(set);
        }
    } else {
        CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);//找到相关mode
        ...
    }
    __CFRunLoopUnlock(rl);
}
static void __CFRunLoopAddItemToCommonModes(const void *value, void *ctx) {
    CFStringRef modeName = (CFStringRef)value;
    CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
    CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]);
    if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) { ... } else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
        ...
    } else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
        CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);//再调用
    }
}

解决:

timer加入到kCFRunLoopCommonModes后,会将timer加入_commonModeItems中,并将timer加入commonModes的所有mode中,所以不管页面滑动还是静止,(在所有Runloop模式中)timer都能生效。

上一篇 下一篇

猜你喜欢

热点阅读