Runloop
本质就是一个Event Loop
的do while
循环,接收->等待->处理
。
目的:
1、保持程序不退出;
2、负责监听事件,时钟,网络,触摸等。
RunLoop
是一个对外的接口在CoreFoundation
里边关于RunLoop
有5
个类:
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopOberverRef
一个RunLoop
有多个mode
,一个mode
包含Source、Observer、Timer
。每次调用RunLoop
的主函数时,只能指定其中一个Mode
,这个Mode
被称作CurrentMode
。如果需要切换Mode
,只能退出Loop
,再重新指定一个Mode
进入。这样做主要为了分隔开不同组的Source、Observer、Timer
使其互不影响。
Mode
官方提供有5
种,一般常用的Mode
有三种:
NSDefaultRunLoopMode 默认模式,在RunLoop没有指定Mode的时候,默认就跑在DefaultMode下。一般情况下APP都运行在这个mode下;
UITrackingRunLoopMode 一般作用于ScrollView滚动的时候的模式,保证滑动的时候不受其他事件影响;
NSRunLoopCommonModes 是一种组合模式包含了以上两种模式。
1、定时加入runloop中
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1、创建Timer 测试3种model
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
//将timer加入到Runloop中
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
}
//测试1
-(void)timerMethod{
static int a = 0;
NSLog(@"测试1:线程%@ - %d",[[NSThread currentThread] name],a++);
}
@end
创建定时器后需要加入runloop
中由runloop
循环检测定时器的执行情况,否则定时器不会执行。如果timer
调用fire
,定时器只执行一次。
2、子线程中加入timer。一条线程能够存活的原因是什么?有任务。
@interface ViewController ()
@property(nonatomic,assign) BOOL finished;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_finished = YES;
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerThread) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
//创建一个runloop不断执行、使用while+flag控制结束
while (self->_finished) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
NSLog(@"结束");
}];
[thread start];
}
//测试2 在线程中的定时器必须保持线程不被释放
-(void)timerThread{
static int a = 0;
NSLog(@"测试2:线程%@ - %d",[[NSThread currentThread] name],a++);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
_finished = NO;
}
@end
若在子线程中使用runloop
需要保持线程是存活状态,保持线程中一直有任务执行,否则线程执行完成后创建的runloop
将被终止释放。因此需要在子线程中调用run
方法保持runloop
一直处于工作状态,如需终止runloop
,设置标志位,在外部触发终止。
3、线程间的通讯
@interface ViewController ()
@property(nonatomic,assign) BOOL finished;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_finished = YES;
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"开始");
//创建一个runloop不断执行、run很难结束任务,使用while+flag控制结束
while (self->_finished) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
}];
[thread start];
[self performSelector:@selector(threadContact) onThread:thread withObject:self waitUntilDone:NO];
}
//测试3
-(void)threadContact{
NSLog(@"测试3:线程通讯 线程%@",[NSThread currentThread]);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
_finished = NO;
}
@end
4、GCD定时器对应runloop中的source,封装了对runloop的使用
@interface ViewController ()
@property (nonatomic,strong)dispatch_source_t timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//GCD 定时器
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
//设置定时器
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 1.0*NSEC_PER_SEC, 0);
//设置回调
dispatch_source_set_event_handler(_timer, ^{
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_resume(_timer);//启动定时器
}
@end
Source
:事件源(输入源)
按照函数调用栈Source
分为两种:
Source0
:非内核事件
Source1
:系统的内核事件
网友评论