RunLoop 的多模式:多模式的 NSTimer 保障顺畅交互
2023-09-21 09:01:52
前言
在之前的文章中,我们了解了RunLoop的基本概念和工作原理,以及如何使用NSTimer在主线程上创建定时器。但在实际开发中,我们经常需要在子线程上使用定时器,比如在后台下载数据、播放音乐等场景。那么,在子线程上使用NSTimer需要注意什么呢?
RunLoop与NSTimer的关系
在子线程上使用NSTimer,就必须开启子线程的RunLoop,否则定时器无法生效。这是因为NSTimer是通过RunLoop来工作的,RunLoop负责管理和调度线程中的任务,包括定时器任务。当RunLoop开启时,它会不断地从待处理事件队列中取出事件并执行,定时器任务就是其中一种事件。因此,如果RunLoop没有开启,定时器任务就无法执行。
RunLoop的多模式
RunLoop同一时间只能运行在一种模式下,当我们滑动tableview/scrollview的时候RunLoop会切换到UITrackingRunLoop模式,此时RunLoop会处理触摸事件,而定时器任务则会被暂停执行。为了保证定时器任务能够在子线程上正常执行,我们需要使用RunLoop的多模式特性。
RunLoop有三种模式:
- Default RunLoop Mode: 这是RunLoop的默认模式,它主要处理普通事件,比如定时器任务、网络请求等。
- UITracking RunLoop Mode: 这种模式主要处理触摸事件,当用户在屏幕上滑动、点击等操作时,RunLoop会切换到此模式。
- Custom RunLoop Mode: 我们可以自定义RunLoop的模式,以便在不同的场景下处理不同的事件。
在子线程上使用NSTimer
为了在子线程上使用NSTimer,我们需要手动开启子线程的RunLoop,并指定RunLoop的模式为Default RunLoop Mode。我们可以使用NSThread类来创建子线程,并在子线程中开启RunLoop。代码如下:
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain) object:nil];
[thread start];
- (void)threadMain {
// 开启RunLoop
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
// 指定RunLoop模式为Default RunLoop Mode
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
// 启动RunLoop
[runLoop run];
}
在子线程的RunLoop开启后,我们就可以使用NSTimer来创建定时器了。代码如下:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFire:) userInfo:nil repeats:YES];
这样,定时器就会在子线程上每隔1秒执行一次timerFire:方法。
结语
通过这篇文章,我们了解了RunLoop的多模式特性,以及如何在子线程上使用NSTimer。在实际开发中,我们可以根据不同的场景选择不同的RunLoop模式,以便保证应用程序的流畅性和响应性。