3. 再谈 timer 之 CFRunLoopTimerRef
作者: Lefe_x
阅读本小集可以参考以前的一篇小集「 定时器引发的思考 」
学习 RunLoop 的知识时如果有一些使用场景,我想对理解 RunLoop 有很大帮助。而 timer 和 RunLoop 息息相关。没有 RunLoop ,timer 不会跑起来。也就是说如果不把 timer 添加到 RunLoop 中,timer 就不会被执行。而且 RunLoop 和线程是一一对应的,如果非主线程的情况下,只有运行 RunLoop 时它才会创建 RunLoop。
在非主线程中创建一个定时器:
[self performSelectorInBackground:@selector(createTimerInOtherThread) withObject:nil];由于函数 createTimerInOtherThread 不在主线程执行,那么可以使用 [NSThread currentThread] 获取当前的线程,使用 CFRunLoopGetCurrent() 获取当前的 RunLoop。由于只有主线程的 RunLoop 才会开启,而其他线程的 RunLoop 需要通过 CFRunLoopRun() 手动开启。
注意这里发现一个诡异的问题。执行 CFRunLoopRun() 后,它后面的代码将在 RunLoop 停止后执行,这是因为 RunLoop 相当于一个循环,循环结束后它后面的代码才会执行。
- (void)createTimerInOtherThread
{
CFAllocatorRef allocator = kCFAllocatorDefault;
CFAbsoluteTime fireDate = CFAbsoluteTimeGetCurrent();
CFTimeInterval interval = 2.0;
CFOptionFlags flag = 0;
CFIndex index = 0;
// 定时器的回调
CFRunLoopTimerCallBack callback = lefexTimerAction;
// 定时器上下文
CFRunLoopTimerContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
// 创建定时器
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(allocator, fireDate, interval, flag, index, callback, &context);
// 获取当前线程的 runlopp,并且开启 runLoop 定时器才能正常执行
threadRunloop = CFRunLoopGetCurrent();
currentThread = [NSThread currentThread];
// 把timer添加到runloop中,timer将会跑起来
CFRunLoopAddTimer(threadRunloop, timer, kCFRunLoopCommonModes);
// 在 run 之后的代码将不会执行
CFRunLoopRun();
// 下面这行打印将在停止 runLoop 后执行。
NSLog(@"runLoop stop");
}定时器跑起来后,它的回调函数将被执行,回调函数将在它所在的 RunLoop 对应的线程中执行。如果 timer 被添加到 mainRunLoop 回调函数在主线程中执行。
void lefexTimerAction(CFRunLoopTimerRef timer, void *info){
NSLog(@"timer called on thread: %@", [NSThread currentThread]);
}当不在使用 timer 时需要释放掉 timer,而其实也可以直接停止 RunLoop 的运行 (通过 CFRunLoopStop(threadRunLoop)),timer 也会停止。
- (void)invalidTimer:(CFRunLoopTimerRef)timer
{
if (timer) {
CFRunLoopTimerInvalidate(timer);
CFRelease(timer);
timer = 0;
}
if (threadRunloop) {
// 如果不暂停 runLoop,当前对象不会释放
CFRunLoopStop(threadRunloop);
threadRunLoop = NULL;}
}Last updated
Was this helpful?