前言
趁辞职还未上班的当,对项目中所使用到的技术做下总结。一来,温故知新,二来,希望也能帮到需要的人。废话不多说,请往下看:
概念:
- 保证应用程序不退出
- 负责所有事件的监听
- 如果没有事件发生,运行循环会进入休眠状态,待事件发生时,再重新启动
- 子线程的运行循环默认是不启动的
内部实现:
一般而言,一个线程只执行一个任务,任务完成后,线程就会退出。runloop提供了这样一种机制:让线程能随时处理事件且不退出。见代码:
void click(int type) {
NSLog(@"正在处理 %d", type);
}
// 在 iOS 程序中,主线程的运行循环是默认启动的,不需要程序员手动创建或者启动!
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
while (YES) {
NSLog(@"请输入选项,0 退出");
int result = -1;
// 阻塞式函数,如果用户不输入,就不会继续后续的代码!
scanf("%d", &result);
NSLog(@"Hello, World! %d", result);
if (result == 0) {
break;
} else {
// 发生事件,处理事件
click(result);
}
}
}
return 0;
}
与线程的关系
runloop 是为线程而生的,没有线程,就没有存在的必要; run loops 是线程的基础架构部分,Cocoa 和 CoreFoundation 都提供了 run loop 对象方便配置和管理线程的 run loop ;每个线程,包括主线程,都有与之对应的 run loop 对象
- 主线程的run loop默认是启动的
- 对其它线程来说,run loop默认是没有启动的,如果你需要更多的线程交互则可以手动配置和启动,如果线程只是去执行一个长时间的已确定的任务则不需要
- 在任何一个 Cocoa 程序的线程中,都可以通过 [NSRunLoop currentRunLoop] 来获取到当前线程的 run loop
遇到的问题:
- 以+ scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?
- 发生原因:
- Runloop 只能运行在一个 mode 下,如果要换 mode ,当前的 loop 也需要停下重新启动成新的。
- 此方法是以 NSDefaultRunLoopMode 模式添加到运行循环的,优先级:NSRunLoopCommonModes > NSDefaultRunLoopMode
- 所以,滑动时 timer 会暂停 - 一般的解决方法:替换以上方法
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
// 表示发生用户交互的时候,时钟仍然会触发!
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
// 调度时钟,默认将时钟以 NSDefaultRunLoopMode 模式添加到运行循环
// self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
}
- (void)updateTimer {
static int num = 0;
// 模拟延时
[NSThread sleepForTimeInterval:1.0];
NSLog(@"%d", num++);
}
但是此方法针对时钟触发的方法非常耗时时,使用 NSRunLoopCommonModes 就会非常卡顿
- 改进的解决方案:
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 在子线程开启时钟!由于子线程的运行循环没有启动,所以没法监听时钟事件
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
// 开启运行循环 - 启动运行循环 - 这句代码就是一个死循环!
// 如果不停止运行循环,不会执行后续的任何代码
// 一旦停止了运行循环,后续代码能够执行,执行完毕后,线程被自动销毁!
CFRunLoopRun();
NSLog(@"come here");
});
}
- (void)updateTimer {
static int num = 0;
// 模拟延时
// [NSThread sleepForTimeInterval:1.0];
NSLog(@"%d %@", num++, [NSThread currentThread]);
// 满足条件后,停止当前的运行循环
if (num == 8) {
// 一旦停止了运行循环,后续代码能够执行,执行完毕后,线程被自动销毁!
CFRunLoopStop(CFRunLoopGetCurrent());
}
}
有关mode
- mode 主要是用来指定事件在运行循环中的优先级。分为:
- NSDefaultRunLoopMode 默认,空闲状态
- UITrackingRunLoopMode ScrollView滑动时
- UIInitializationRunLoopMode 启动时
- NSRunLoopCommonModes Mode集合 - 苹果公开提供的 mode 有两个
- NSDefaultRunLoopMode
- NSRunLoopCommonModes
其他用途
- 利用RunLoop空闲时间执行预缓存任务(详见 UITableView+FDTemplateLayoutCell 第三方框架)
网友评论