深入浅出 RunLoop(五):RunLoop 与 NSTimer

2020/2/28 23:15:26

本文主要是介绍深入浅出 RunLoop(五):RunLoop 与 NSTimer,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

RunLoop 系列文章

深入浅出 RunLoop(一):初识
深入浅出 RunLoop(二):数据结构
深入浅出 RunLoop(三):事件循环机制
深入浅出 RunLoop(四):RunLoop 与线程
深入浅出 RunLoop(五):RunLoop 与 NSTimer
深入浅出 RunLoop(六):相关面试题

RunLoop 与 NSTimer

  • 由前面的文章我们知道,NSTimer是由RunLoop来管理的,NSTimer其实就是CFRunLoopTimerRef,他们之间是 toll-free bridged 的,可以相互转换;
  • 如果我们在子线程上使用NSTimer,就必须开启子线程的RunLoop,否则定时器无法生效。

解决 tableview 滑动时 NSTimer 失效的问题

  • 问题:由前面的文章我们知道,RunLoop同一时间只能运行在一种模式下,当我们滑动tableview/scrollview的时候RunLoop会切换到UITrackingRunLoopMode界面追踪模式下。如果我们的NSTimer是添加到RunLoopKCFRunLoopDefaultMode/NSDefaultRunLoopMode默认模式下的话,此时是会失效的。
  • 解决:我们可以将NSTimer添加到RunLoopKCFRunLoopCommonModes/NSRunLoopCommonModes通用模式下,来保证无论在默认模式还是界面追踪模式下NSTimer都可以执行。
  • NSTimer的创建方式

如果我们是通过以下方法创建的NSTimer,是默认添加到RunLoop的默认模式下的

    [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"123");
    }];
复制代码

我们可以通过以下方法创建NSTimer,来自定义添加到RunLoop的某种模式下

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

CFRunLoopAddTimer 函数实现

CFRunLoopAddTimer()函数中会判断传入的modeName模式名称是不是 kCFRunLoopCommonModes通用模式,是的话就会将timer添加到RunLoop的 _commonModeItems 集合中,并同步该timer到 _commonModes 里的所有模式中,这样无论在默认模式还是界面追踪模式下NSTimer都可以执行。

void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {    
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return;
    if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
    __CFRunLoopLock(rl);
    if (modeName == kCFRunLoopCommonModes) {       // 判断 modeName 是不是 kCFRunLoopCommonModes
	    CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
	    if (NULL == rl->_commonModeItems) {    // 懒加载,判断 _commonModeItems 是否为空,是的话创建
	        rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
    	}
    	CFSetAddValue(rl->_commonModeItems, rlt);  // 将 timer 添加到 _commonModeItems 中
    	if (NULL != set) {
	        CFTypeRef context[2] = {rl, rlt};  // 将 timer 和 RunLoop 封装到 context 中
    	    /* add new item to all common-modes */
            // 遍历 commonModes,将 timer 添加到 commonModes 的所有模式下
    	    CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
    	    CFRelease(set);
    	}
    ......
	}
}


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) =
                   

这篇关于深入浅出 RunLoop(五):RunLoop 与 NSTimer的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程