什么是RunLoop

RunLoop从字面上看是运行循环的意思,这一点也不错,它确实就是一个循环的概念,或者准确的说是线程中的循环。 本文一开始就提到有些程序是一个圈,这个圈本质上就是这里的所谓的RunLoop,就是一个循环,只是这个循环里加入很多特性。

RunLoop中内部结构

一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。

RunLoop 的内部逻辑

内部代码整理:

  1. /// 用DefaultMode启动
  2. void CFRunLoopRun(void) {
  3. CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
  4. }
  5. /// 用指定的Mode启动,允许设置RunLoop超时时间
  6. int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) {
  7. return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
  8. }
  9. /// RunLoop的实现
  10. int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {
  11. /// 首先根据modeName找到对应mode
  12. CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false);
  13. /// 如果mode里没有source/timer/observer, 直接返回。
  14. if (__CFRunLoopModeIsEmpty(currentMode)) return;
  15. /// 1. 通知 Observers: RunLoop 即将进入 loop。
  16. __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);
  17. /// 内部函数,进入loop
  18. __CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {
  19. Boolean sourceHandledThisLoop = NO;
  20. int retVal = 0;
  21. do {
  22. /// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。
  23. __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);
  24. /// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。
  25. __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);
  26. /// 执行被加入的block
  27. __CFRunLoopDoBlocks(runloop, currentMode);
  28. /// 4. RunLoop 触发 Source0 (非port) 回调。
  29. sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);
  30. /// 执行被加入的block
  31. __CFRunLoopDoBlocks(runloop, currentMode);
  32. /// 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。
  33. if (__Source0DidDispatchPortLastTime) {
  34. Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)
  35. if (hasMsg) goto handle_msg;
  36. }
  37. /// 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。
  38. if (!sourceHandledThisLoop) {
  39. __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);
  40. }
  41. /// 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。
  42. /// • 一个基于 port 的Source 的事件。
  43. /// • 一个 Timer 到时间了
  44. /// • RunLoop 自身的超时时间到了
  45. /// • 被其他什么调用者手动唤醒
  46. __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {
  47. mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg
  48. }
  49. /// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。
  50. __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);
  51. /// 收到消息,处理消息。
  52. handle_msg:
  53. /// 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。
  54. if (msg_is_timer) {
  55. __CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())
  56. }
  57. /// 9.2 如果有dispatch到main_queue的block,执行block。
  58. else if (msg_is_dispatch) {
  59. __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
  60. }
  61. /// 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件
  62. else {
  63. CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);
  64. sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);
  65. if (sourceHandledThisLoop) {
  66. mach_msg(reply, MACH_SEND_MSG, reply);
  67. }
  68. }
  69. /// 执行加入到Loop的block
  70. __CFRunLoopDoBlocks(runloop, currentMode);
  71. if (sourceHandledThisLoop && stopAfterHandle) {
  72. /// 进入loop时参数说处理完事件就返回。
  73. retVal = kCFRunLoopRunHandledSource;
  74. } else if (timeout) {
  75. /// 超出传入参数标记的超时时间了
  76. retVal = kCFRunLoopRunTimedOut;
  77. } else if (__CFRunLoopIsStopped(runloop)) {
  78. /// 被外部调用者强制停止了
  79. retVal = kCFRunLoopRunStopped;
  80. } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {
  81. /// source/timer/observer一个都没有了
  82. retVal = kCFRunLoopRunFinished;
  83. }
  84. /// 如果没超时,mode里没空,loop也没被停止,那继续loop。
  85. } while (retVal == 0);
  86. }
  87. /// 10. 通知 Observers: RunLoop 即将退出。
  88. __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
  89. }

可以看到,实际上 RunLoop 就是这样一个函数,其内部是一个 do-while 循环。当你调用 CFRunLoopRun() 时,线程就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回。

转载:

http://blog.ibireme.com/2015/05/18/runloop/(深入理解runloop,里面还有几个实际使用例子的简单介绍)

http://www.starming.com/index.php?v=index&view=74(简单介绍了runloop机制,通过几个例子,进行讲解,和前面一篇有一些重复)

http://www.dreamingwish.com/frontui/article/default/ios-multithread-program-runloop-the.html(runloop代码实现)

iOS--RunLoop原理介绍的更多相关文章

  1. iOS runLoop 原理多线程 总结 NSTimer优化

    可以理解为字面意思:Run 表示运行,Loop 表示循环.结合在一起就是运行的循环的意思.哈哈,我更愿意翻译为『跑圈』.直观理解就像是不停的跑圈. RunLoop 实际上是一个对象,这个对象在循环中用 ...

  2. 【Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析】

    原文:[Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析] [注意:]团队里总是有人反映卸载Xamarin,清理不完全.之前写过如何完全卸载清理剩余的文件.今天写了Windows下的批命令 ...

  3. iOS定位原理和使用建议(转)

    原文:http://ibbs.91.com/thread-1548870-1-1.html 看到很多网友讨论iOS设备定位的问题,这里将我们所了解的关于iPhone.iPad.iPod等的定位原理做详 ...

  4. iOS RunLoop详解

    1. RunLoop简介 1.1 什么是RUnLoop 可以理解为字面的意思:Run表示运行,Loop表示循环.结合在一起就是运行的循环.通常叫做运行循环. RunLoop实际上是一个对象,这个对象在 ...

  5. UIContainerView纯代码实现及原理介绍

    UIContainerView纯代码实现及原理介绍 1.1-在StoryBoard中使用UIContainerView 1.2-纯代码使用UIContainerView 1.3-UIContainer ...

  6. 03 Yarn 原理介绍

    Yarn 原理介绍 大纲: Hadoop 架构介绍 YARN 产生的背景 YARN 基础架构及原理   Hadoop的1.X架构的介绍   在1.x中的NameNodes只可能有一个,虽然可以通过Se ...

  7. 04 MapReduce原理介绍

    大数据实战(上) # MapReduce原理介绍 大纲: * Mapreduce介绍 * MapReduce2运行原理 * shuffle及排序    定义 * Mapreduce 最早是由googl ...

  8. Android Animation学习(一) Property Animation原理介绍和API简介

    Android Animation学习(一) Property Animation介绍 Android Animation Android framework提供了两种动画系统: property a ...

  9. [转]MySQL主从复制原理介绍

    MySQL主从复制原理介绍 一.复制的原理 MySQL 复制基于主服务器在二进制日志中跟踪所有对数据库的更改(更新.删除等等).每个从服务器从主服务器接收主服务器已经记录到其二进制日志的保存的更新,以 ...

  10. iOS Simulator功能介绍关于Xamarin IOS开发

    iOS Simulator功能介绍关于Xamarin IOS开发 iOS Simulator功能介绍 在图1.38所示的运行效果中,所见到的类似于手机的模型就是iOS Simulator.在没有iPh ...

随机推荐

  1. SpringMVC源码阅读(一)

    DispatcherServlet是整个SpringMVC初始化和处理请求的重要类,作为一个servlet,拥有 public void init(ServletConfig config) thro ...

  2. Hibernate中的一对一关系详解(1)

    A:先讲讲一对一的关系(欲知其他关系,请看下篇) a:主键关联的一对一关系 一对一关系一般用主键关联,也就是说用主键值来维护两者的关系,一个表的主键存放另一个表的主键值.例如在员工与帐号中,我们取员工 ...

  3. C连接MySQL数据库开发之Windows环境配置及测试

    一.开发环境 Win8.1 64位.VS2013.MySQL5.5.3764位 MySQL安装目录为:C:\Program Files\MySQL\MySQL Server 5.5 二.配置工程环境 ...

  4. DPI/PPI/dp/sp/px/pt 移动设计手册

    转自DPI/PPI/dp/sp/px/pt 移动设计手册 做移动设计的同学,不管是原生app或者web app,应该对字体字号都是很头痛的问题.根本原因是,我们用唯一分辨率的电脑,设计各个不同尺寸大小 ...

  5. Cow Marathon

    poj1985:http://poj.org/problem?id=1985 题意:就是树的直径. 题解:直接DFS即可. #include<iostream> #include<c ...

  6. 【POJ1067】取石子游戏 (威佐夫博弈)

    [题目] Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的 ...

  7. [LeetCode#252] Meeting Rooms

    Problem: Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2] ...

  8. lambda -- Filter Java Stream to 1 and only 1 element

    up vote10down votefavorite I am trying to use Java 8 Streams to find elements in a LinkedList. I wan ...

  9. Windows Azure云服务价格调整通知

     好消息!由世纪互联运营的 Windows Azure推出优惠啦.我们采纳了多渠道客户的意见和建议,为了更好地服务大家,将降低多种云服务的价格,其中包括我们最受欢迎的服务 -虚拟机和 Block ...

  10. lemon OA 下阶段工作安排

    lemon OA 下阶段工作安排 经验总结 lemon OA系统作为一个中型的java web系统,在架构上还是有着很好地可学习的地方.但是由于经验不足,过程比较迂回.如果真的有经验的话,应该可以做到 ...