1.RunLoop使用
RunLoop又称运行循环,保证app程序一直运行的的基础。
一般程序在运行完代码后就结束运行,要时程序一直运行,则需要一个while循环,保证程序不退出。RunLoop就是这样的角色存在,在有任务时处理任务,没有任务时进入休眠状态。
在iOS开发中,RunLoop与线程为一一对应的关系,每个线程有自己的RunLoop,线程与RunLoop的关系保存在一个全局字典中,key为线程,value为RunLoop。主线程RunLoop为系统启动,无需程序员管理,子线程的RunLoop为懒加载,需要程序员主动调用才会创建,否则子线程执行完任务就退出。
苹果官方的RunLoop示意图如下:
官方文档
简单创建一个线程并激活RunLoop代码如下:
@interface ViewController ()
@property(nonatomic,weak)NSThread* thread;
@end
@implementation ViewController
- (void)ThreadRun:(id)object {
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"%s %@",__FUNCTION__,object);
}
- (void)viewDidLoad {
[super viewDidLoad];
NSThread *thread = [[NSThread alloc] initWithBlock:^{
//添加port才能激活
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//添加定时器也可以激活
//NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
//NSLog(@"block run");
//}];
//[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}];
[thread start];
self.thread = thread;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//可以在该线程不断添加新任务
[self performSelector:@selector(ThreadRun:) onThread:self.thread withObject:self waitUntilDone:NO];
}
@end
RunLoop每次运行需要处理的事件分为3类:Observer监听事件,Timer定时器事件与Source输入源事件。其中,Source输入源事件又可以分为Source0和Source1:Source0事件不会主动触发,需要将其标记为待处理之后手动唤醒RunLoop进行处理;Source1事件会主动唤醒RunLoop进行处理,通常用来线程间通信和处理硬件接口的信号。Timer事件特指定时器的回调,当定时器被添加进RunLoop时,会根据设置的定时器频率在RunLoop中注册一系列的时间点,当时间点到时会唤醒RunLoop进行事件处理。Observer事件用来监听RunLoop状态的变化,当RunLoop状态变化时会触发对应的回调。
有需要的时候我们可以添加监听,方法如下,可以在排查app卡顿的时候使用。
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"%lu",activity);
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
2.RunLoop的模式
RunLoop可以通过模式来筛选其要处理的事件,NSRunLoopMode为字符串类型,用户除了可以使用系统设置的Mode,也可以自定义Mode
typedef NSString * NSRunLoopMode NS_TYPED_EXTENSIBLE_ENUM;
供用户使用的mode如下,当我们需要在滑动页面添加定时器时,可以把定时器添加到NSRunLoopCommonModes。
FOUNDATION_EXPORT NSRunLoopMode const NSDefaultRunLoopMode;
FOUNDATION_EXPORT NSRunLoopMode const NSRunLoopCommonModes API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
系统使用的mode如下,这些mode我们无需使用。
NSRunLoopCommonModes //A pseudo-mode that includes one or more other run loop modes.
NSDefaultRunLoopMode //The mode set to handle input sources other than connection objects.
NSEventTrackingRunLoopMode //The mode set when tracking events modally, such as a mouse-dragging loop.
NSModalPanelRunLoopMode //The mode set when waiting for input from a modal panel, such as a save or open panel.
UITrackingRunLoopMode //The mode set while tracking in controls takes place.