iOS知识梳理 - runloop

iOS知识梳理 – runloop

runloop其实是个很普遍的东西,基本上是个应用框架都有类似的东西,比如js或flutter里的event loop,Android的looper。

下面我们从最简单的例子来看一下,runloop的本质

溯源

最初学编程的时候,有个经典题目是用c/c++实现四则运算。大体上要下面这个可以一直输入的效果:

《iOS知识梳理 - runloop》

代码大概像这样:

int main()
{
    string str;
    double result;//保存结果
    while(getline(cin,str))
    {
    // calculate
    cout << "Result is: " << result << endl;
    }
}

由于可以一直输入新的算式,我们自然地使用了一个while循环来处理输入,有新的一行输入就去计算结果并输出,没有新的输入时其实就是阻塞等待。

这其实就是最简单的runloop。

实际应用程序中,无非就是输入的形式更多了一些,执行的任务更复杂了一些,基本框架,其实还是一个while循环。

网上看到一个伪代码挺清晰的:

function loop() {
  initialize();
  do {
      var message = get_next_message();
      process_message(message);
  } while (message != quit);
}

深入理解runloop

runloop的实际实现还是有大量细节的,core foundation框架也是开源的,这里其实可以挖得很深。

iOS 多线程:『RunLoop』详尽总结,这篇文章讲得挺好,推荐阅读。很多细节本文就不再做太多展开了。

Mode

runloop在运行时有个mode的概念,runloop只会处理当前mode的event。

比如,我们有mode1和mode2,mode1对应了3个event,mode2对应了2个event,如果当前runloop处于mode1,就只会处理mode1对应的3个event。

具体而言,runloop暴露出来的是kCFRunLoopDefaultMode和UITrackingRunLoopMode两个mode,通常主线程处于DefaultMode,但是当滑动scrollview时,主线程处于TrackingMode。而默认地,我们的NSTimer事件是绑定在DefaultMode的,这就会导致,当滑动scrollview时,就不会触发NSTimer事件。

为了解决这种问题,runloop给出了一个CommonMode的概念,CommonMode不是一个具体的Mode,可以理解成一组Mode的集合,这里实际上就是DefaultMode和TrackingMode。把Timer加入到CommonMode,则在DefaultMode和TrackingMode都会执行这个Timer了。

Mode这个东西,在实际使用中,除了给Timer的使用带来困扰之外,几乎从未被使用过。

那么,为什么苹果会在Runloop中设计Mode这个东西?

网上并没有找到有价值的讨论,不过可以简单揣测一下,目的可能是,在某些场景下,可以只处理对应场景需要的事件。这可能是基于一种特殊情况的假设:在某种场景下,CPU忙不过来,因此需要停掉其它任务,专注于当前场景的任务。比如,任务A需要90%的CPU,其它杂七杂八事件处理需要40%的CPU,那么,停掉那些杂事专注当前场景,可能是有意义的行为。

如果CPU并不会跑满的话,那么Mode的拆分就意义不大,充其量是副作用极大的优先级调整(低优先级的直接被卡住了诶)。

参考其它平台的EventLoop实现,通常只是提供简单的优先级设置能力即可。

总的来说,个人觉得Mode的设计是比较鸡肋的。

子线程的Runloop

默认地,子线程并不会开启Runloop。想一想,这是非常合理的。开启了Runloop后,即使在没有事件时,线程也会进入阻塞等待的状态,需要的时候那叫保活,不需要的时候就是白占资源了。因此,子线程默认情况下是肯定不能开启Runloop的。而没有开启Runloop的情况下,涉及到Runloop的异步调用就都不能用了,主要就是NSTimer,performSelector:afterDelay:,网络请求的回调等。

解决方案是通过[[NSRunLoop currentRunLoop] run]启动当前线程的runloop。

跟Runloop相关的特性

  1. autoreleasepool:每次runloop开始时进入pool,结束时离开pool。从而在未显式声明autoreleasepool的时候释放autorelease对象。
  2. 界面更新:操作UI时会把对应的UIView/CALayer标记为待处理,在每次迭代即将结束时会执行实际的绘制。
    原文作者:二师兄
    原文地址: https://segmentfault.com/a/1190000020606764
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注