Runloop :运行循环
- APP启动,操作系统会开启一条线程,这就是这个APP的主线程;
- 这个主线程是一个常驻线程,因为这条线程上边的Runloop 被开启了;
Runloop 作用
- 保证线程不退出;
- 负责监听所有的事件。 如: 触摸、时钟、网络事件...
Runloop 的模式(Mode)
- NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeSelect) userInfo:nil repeats:YES];
- [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeSelect) userInfo:nil repeats:YES];
以上这两个timer的实现, 结果是: 函数1不会执行,函数2会执行。原因就是函数2内部已经添加了runLoop。而函数1, 需要手动添加。 那么函数1需要再添加: [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 即可执行time操作。
- Runloop共有5中模式:(常用的其实只有两种)
- NSDefaultRunLoopMode // 默认模式
- UITrackingRunLoopMode // UI模式, 优先级高于默认模式
- NSRunLoopCommonModes // 占位模式,(并不是runloop 的真正模式,但功能相当于 默认模式+UI模式)
- 程序初始化模式 // 操作系统调用
- 程序内核模式 // 操作系统调用
NSDefaultRunLoopMode : 首先在没有事件发生时候runloop处于休眠状态,当有事件需要执行或响应, runloop会被唤醒做一次执行循环,执行结束继续处于休眠状态。 如果此时有触摸事件发生( 比如 拖拽事件或者触摸事件)那么runloop 会优先处理触摸事件,而忽略timer事件。timer 的selector 也不会被执行。当松开手 timer 会继续执行。
UITrackingRunLoopMode :简称 UI模式, runloop 只会触发UI事件,当触摸事件发生的时候才runloop 才会执行。而此时 默认模式即使有事件执行,runloop也不会理会。
NSRunLoopCommonModes: 占位模式,并不是lunloop真正的模式。 所谓占位指的是在添加了NSDefaultRunLoopMode 模式,也添加了TrackingRunLoopMode 模式。 那么此时表现也是二合一。 正常执行的timer在发生触摸时也会继续执行。但是!!!timer 中如果有耗时操作, 将会造成UI页面卡顿。 怎么解决呢? 当然是放在子线程中!
Runloop 与多线程
NSThread *tread = [[NSThread currentThread]initWithBlock:^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeSelect) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop]run];
}];
[tread start];
这样 当前子线程的runloop 会运行起来!子线程会一直存在,即使timer停止。 相当于一个死循坏!!!
那么要想销毁当前子线程,记得在子线程中调用 [NSThread exit];
如果在主线程中调用 [NSThread exit], 那么主线程会退出, 子线程依旧还在运行。随着主线程的退出,页面点击、拖拽等事件都不会再响应。那为什么主线程挂掉之后为什么UI页面都还在显示? 因为UI显示是显示在操作系统上,UIApplication无法处理界面上的事情了,而界面是传递给主线程,主线程已挂断掉 此时runloop 不在处理界面上任何事情。
Runloop 的source
source: 事件源(输入源)按照函数调用栈分为
source0:非系统内核事件;
source1: 系统内核事件;
GCD timer, 直接上代码吧
// 创建
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
// 设置timer 时间单位是纳秒 1秒 = 1000000000纳秒
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1.0*NSEC_PER_SEC, 0);
// 设置回调
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"current thread --- %@",[NSThread currentThread]);
});
// 启动
dispatch_resume(self.timer);
根据执行结果可知, GCD timer 默认已经将Runloop 添加进去了。
Runloop 的Observer
用于观察runloop循环, 代码 :
- (void)viewDidLoad {
[super viewDidLoad];
// 防止runloop 休眠
[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(timerMeather) userInfo:nil repeats:YES];
// 添加观察者
[self addRunLoopObserver];
}
-(void)timerMeather
{
}
-(void)addRunLoopObserver{
// 获取当前RunLoop
CFRunLoopRef runloop = CFRunLoopGetCurrent();
// 定义一个context
CFRunLoopObserverContext context = {
0,
(__bridge void *)self,
&CFRetain,
CFRelease,
NULL
};
// 定义观察者
static CFRunLoopObserverRef defaultModeObserver;
//创建观察者
defaultModeObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, 0, &callBack, &context);
// 添加观察者
CFRunLoopAddObserver(runloop, defaultModeObserver, kCFRunLoopDefaultMode);
}
// 观察runloop回调
static void callBack(CFRunLoopObserverRef obderver,CFRunLoopActivity activity ,void *info){
// info 当前控制器, 添加观察者时添加的context目的。
NSLog(@"come ");
}
添加代码会发现,刚启动的时候回执行几次,然后就不执行了,或者偶尔执行以下。 那是因为runloop 没任务执行,处于休眠状态。 那么我们给执行一个timer,让timer运行起来。 此时runloop 也就不会休眠了。