深刻浅出 RunLoop(一):初识
深刻浅出 RunLoop(二):数据结构
深刻浅出 RunLoop(三):事件循环机制
深刻浅出 RunLoop(四):RunLoop 与线程
深刻浅出 RunLoop(五):RunLoop 与 NSTimer
深刻浅出 RunLoop(六):相关面试题面试
NSTimer
是由RunLoop
来管理的,NSTimer
其实就是CFRunLoopTimerRef
,他们之间是 toll-free bridged 的,能够相互转换;NSTimer
,就必须开启子线程的RunLoop
,不然定时器没法生效。RunLoop
同一时间只能运行在一种模式下,当咱们滑动tableview
/scrollview
的时候RunLoop
会切换到UITrackingRunLoopMode
界面追踪模式下。若是咱们的NSTimer
是添加到RunLoop
的KCFRunLoopDefaultMode
/NSDefaultRunLoopMode
默认模式下的话,此时是会失效的。NSTimer
添加到RunLoop
的KCFRunLoopCommonModes
/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()
函数中会判断传入的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) == CFRunLoopSourceGetTypeID()) {
CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
}
}
复制代码
NSTime
和CADisplayLink
底层都是基于RunLoop
的CFRunLoopTimerRef
的实现的,也就是说它们都依赖于RunLoop
。若是RunLoop
的任务过于繁重,会致使它们不许时。NSTimer
每1.0秒就会执行一次任务,Runloop
每进行一次循环,就会看一下NSTimer
的时间是否达到1.0秒,是的话就执行任务。可是因为Runloop
每一次循环的任务不同,所花费的时间就不固定。假设第一次循环所花时间为 0.2s,第二次 0.3s,第三次 0.3s,则再过 0.2s 就会执行NSTimer
的任务,这时候可能Runloop
的任务过于繁重,第四次花了0.5s,那加起来时间就是 1.3s,致使NSTimer
不许时。RunLoop
,因此它很是的准时。示例以下:dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
//建立定时器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//设置时间(start:几s后开始执行; interval:时间间隔)
uint64_t start = 2.0; //2s后开始执行
uint64_t interval = 1.0; //每隔1s执行
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0);
//设置回调
dispatch_source_set_event_handler(timer, ^{
NSLog(@"%@",[NSThread currentThread]);
});
//启动定时器
dispatch_resume(timer);
NSLog(@"%@",[NSThread currentThread]);
self.timer = timer;
/* 2020-02-01 21:34:23.036474+0800 多线程[7309:1327653] <NSThread: 0x600001a5cfc0>{number = 1, name = main} 2020-02-01 21:34:25.036832+0800 多线程[7309:1327705] <NSThread: 0x600001acb600>{number = 7, name = (null)} 2020-02-01 21:34:26.036977+0800 多线程[7309:1327705] <NSThread: 0x600001acb600>{number = 7, name = (null)} 2020-02-01 21:34:27.036609+0800 多线程[7309:1327707] <NSThread: 0x600001a1e5c0>{number = 4, name = (null)} */
复制代码