由 RunLoop 想到的

RunloopiOS 提供的一个事件处理机制,当无事件发生时,当前线程会休眠,只有当事件(比如触摸)到来时才会触发相应的操作,本质是为了节省更多的 CPU。

Runloop 的模型和 Node 的异步 IO 模型很像,都是一个线程去循环的处理事件,如果事件需要的是 IO 密集型操作的话,就可以异步的去执行,而不会阻塞当前线程的执行,如果事件需要的是 CPU 密集型操作的话,就会阻塞当前线程,所以,这种模型适用于 IO 密集型比较多的场景,能极大的利用单线程的能力提高并发量。但是 iOS其实是可以提供多线程方案的,所以即使有 CPU 密集型的操作时也可以分配到其他线程来完成。

我们写代码时是不需要关心 Runloop 的存在的,但是代码的执行过程却和 Runloop 息息相关,其中包括对象的自动释放,图形的绘制和渲染等。

针对于 UI 的创建或者改动,其实是不会马上在屏幕上显示出来的,这些改动会合并到一个集合中,等到 Runloop 进入休眠前或者退出时才显示出来。这里用到的机制就是 CATransactionCATransaction 会在 Runloop 中监听休眠前和退出时的事件,一旦有该事件到来,它自动将集合中的改动提交到 GPU 去显示。

这里要提到我们经常遇到的卡顿问题,卡顿问题与 Runloop 有关吗?当然有关,那么是怎么引起的呢?Runloop 在启动后会注册对应的 CFRunLoopSource 通过 mach_port 接收显示服务传过来的时钟信号通知(通常每秒60次,这就表示 Runloop 至少会 1/60s 运行一次,因为每次都会被唤醒),然后在回调中会读取 GPU 生成的帧缓冲区中的数据,显示到屏幕上,而现在一般都是双缓冲区,读取完一个缓冲区显示完数据后再读取另一个缓冲区,这时就存在显示完之后另一个缓冲区数据没准备好的情况,这时还会显示上一帧,导致了卡顿的问题。那么什么时候会导致缓冲区数据没准备好呢,这就是因为在一个 Runloop 中有过多的 CPU 运算或者过多的 GPU 运算,导致在一个周期内无法填充到帧缓冲区。其实最好的时间是 CPU+GPU 的运算时间不超过1/60s=16.7ms。

对象的自动释放用到了 AutoReleasePool,而 AutoReleasePool 会监听进入和离开 Runloop 的事件,会在这两个事件发生时释放对象。

参考文章:
http://vizlabxt.github.io/blog/2012/11/17/Understanding-Runloop/
http://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/

你可能感兴趣的:(由 RunLoop 想到的)