概念
数据结构
事件循环机制
RunLoop和NStimer
RunLoop和多线程
RunLoop是通过内部维护的事件循环来对事件/消息进行管理的一个对象
没有消息需要处理时,休眠以避免资源占用
用户态-》核心态
有消息处理时,立即被唤醒
核心态-〉用户态
CFRunLoop
CFRunLoopMode
Source/Timer/Observer
source0
需要手动唤醒线程
source1
具备唤醒线程的能力
CFRunLoopObserver
观测时间点
kCFRunLoopEntry
kCFRunLoopBeforeTimers
CommonMode的特性
NSRunLoopCommonModes
commonMode不是实际存在的一种mode
是同步Source/Timer/Observer到多个Mode中的一种技术方案
如何实现一个常驻线程
给子线程添加RunLoop
@autoreleasepool {
// 子线程对应的runloop需要自己创建并开启
// 创建子线程对应的runloop,使子线程一直存在
NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
// 给runloop添加一个基于port的事件(系统事件),让runloop的运行模式不为空,保证runloop不退出
[currentRunloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
// 开启运行循环
[currentRunloop run];
}
利用 runloop 解释一下页面的渲染的过程?
当我们调用 [UIView setNeedsDisplay] 时,这时会调用当前 View.layer 的 [view.layer setNeedsDisplay]方法。
这等于给当前的 layer 打上了一个脏标记,而此时并没有直接进行绘制工作。而是会到当前的 Runloop 即将休眠,也就是 beforeWaiting 时才会进行绘制工作。
紧接着会调用 [CALayer display],进入到真正绘制的工作。CALayer 层会判断自己的 delegate 有没有实现异步绘制的代理方法 displayer:,这个代理方法是异步绘制的入口,如果没有实现这个方法,那么会继续进行系统绘制的流程,然后绘制结束。
CALayer 内部会创建一个 Backing Store,用来获取图形上下文。接下来会判断这个 layer 是否有 delegate。
如果有的话,会调用 [layer.delegate drawLayer:inContext:],并且会返回给我们 [UIView DrawRect:] 的回调,让我们在系统绘制的基础之上再做一些事情。
如果没有 delegate,那么会调用 [CALayer drawInContext:]。
以上两个分支,最终 CALayer 都会将位图提交到 Backing Store,最后提交给 GPU。
至此绘制的过程结束。
二 你在开发过程中怎么使用RunLoop?什么应用场景?
开启一个常驻线程(让一个子线程不进入消亡状态,等待其他线程发来的消息,处理其他事件)
在子线程中开启一个定时器
在子线程中进行一些长期监控
可以控制定时器在特定模式下运行
可以让某些事件(行为,任务)在特定模式下执行
可以添加observer监听RunLoop的状态,比如监听点击事件的处理(比如在所有点击事件前做一些处理)
1)NSTimer
2)ImageView显示:控制方法在特定的模式下可用
3)PerformSelector
4)常驻线程:在子线程中开启一个runloop
5)自动释放池
第一次创建:进入runloop的时候
最后一次释放:runloop退出的时候
其它创建和释放:当runloop即将休眠的时候会把之前的自动释放池释放,然后重新创建一个新的释放池
// 高性能定时器
__block int timeout=300; //倒计时时间
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
if(timeout<=0){ //倒计时结束,关闭
dispatch_source_cancel(_timer);
dispatch_async(dispatch_get_main_queue(), ^{
//设置界面的按钮显示 根据自己需求设置
NSLog(@”=====”)
});
}else{
int minutes = timeout / 60;
int seconds = timeout % 60;
NSString *strTime = [NSString stringWithFormat:@"%d分%.2d秒后重新获取验证码",minutes, seconds];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"*******");
});
timeout--;
}
});
dispatch_resume(_timer);