iOS面试iOS学习

Cocoa RunLoop 系列之基础知识

2016-12-08  本文已影响51人  IcebergHorseman

博客地址

这篇博客主要结合Apple开发者文档和个人的理解,写的一篇关于Cocoa RunLoop基本知识点的文章。在文档的基础上,概况和梳理了RunLoop相关的知识点。

一、Event Loop & Cocoa RunLoop

宏观上:Event Loop

  1. RunLoop是一个用于循环监听和处理事件或者消息的模型,接收请求,然后派发给相关的处理模块,wikipedia上有更为全面的介绍:Event_loop
  2. Cocoa RunLoop属于Event Loop模型在Mac平台的具体实现
  3. 其他平台的类似实现:X Window程序,Windows程序 ,Glib库等

微观上: Cocoa RunLoop

  1. Cocoa RunLoop本质上就是一个对象,提供一个入口函数启动事件循环,在满足特点条件后才会退出。
  2. Cocoa RunLoop与普通while/for循环不同的是它能监听处理事件和消息,能智能休眠和被唤醒,这些功能的其实现依赖于Mac Port。

二、 Cocoa RunLoop的内部结构

但凡说到Cocoa RunLoop内部结构,都离不开下面这张图,来源于Apple开发者文档

图1-1 RunLoop结构图

结合上图,可将RunLoop架构划分为四个部分:

  1. 事件源
  2. 运行模式
  3. 循环机制
  4. 执行反馈

1. 事件源

Cocoa RunLoop接受的事件源分为两种类型:Input Sources 和 Timer Sources

1.1. Input Sources

Input Sources通过异步派发的方式将事件转送到目标线程,事件类别分为两大块:


   //部署在主线程
   //参数列表:Selector:事件源处理函数,Selector参数,是否阻塞当前线程,指定RunLoop模式
   performSelectorOnMainThread:withObject:waitUntilDone:
   performSelectorOnMainThread:withObject:waitUntilDone:modes:
   
   //部署在指定线程
   //参数列表:Selector:事件源处理函数,指定线程,Selector参数,是否阻塞当前线程,指定RunLoop模式
   permSelector:onThread:withObject:waitUntilDone:
   performSelector:onThread:withObject:waitUntilDone:modes:
   
   //部署在当前线程
   //参数列表:Selector:事件源处理函数,Selector参数,延时执行时间,指定RunLoop模式
   performSelector:withObject:afterDelay:
   performSelector:withObject:afterDelay:inModes:
    
   //撤销某个对象通过函数performSelector:withObject:afterDelay:部署在当前线程的全部或者指定事件源
   cancelPreviousPerformRequestsWithTarget:
   cancelPreviousPerformRequestsWithTarget:selector:object:

综上,Input Sources包括基于Mach端口的事件源和自定义的事件源,二者的唯一区别在于被触发的方式:前者是由内核自动触发,后者则需要在其他线程中手动触发。

1.2. Timer Sources

不同于Input Sources的异步派发,Timer Source是通过同步派发的方式,在预设时间到达时将事件转送到目标线程。这种事件源可用于线程的自我提醒功能,实现周期性的任务。

2. 运行模式

运行模式类似于一个过滤器,用于屏蔽那些不关心的事件源,让RunLoop专注于监听和处理指定的事件源和RunLoop Observer。

CFRunLoopMode 和 CFRunLoop 的数据结构大致如下:


    struct __CFRunLoop {
        CFMutableSetRef _commonModes;     // Set
        CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
        CFRunLoopModeRef _currentMode;    // Current Runloop Mode
        CFMutableSetRef _modes;           // Set
        ...
    };
    
    struct __CFRunLoopMode {
        CFStringRef _name;            // Mode Name, 例如 @"kCFRunLoopDefaultMode"
        CFMutableSetRef _sources0;    // Set
        CFMutableSetRef _sources1;    // Set
        CFMutableArrayRef _observers; // Array
        CFMutableArrayRef _timers;    // Array
        ...
    };

结合以上源码,总结以下几点:

** Note ** : 不同的运行模式区别在于事件源的不同,比如来源于不同端口的事件和端口事件与Timer事件。不能用于区分不同的事件类型,比如鼠标消息事件和键盘消息事件,因为这两种事件都属于基于端口的事件源。

以下是苹果预定义好的一些运行模式:

3. 循环机制

循环机制涉及两方面:

3.1. RunLoop与线程之间的关系

Apple文档中提到:开发者不需要手动创建RunLoop对象,每个线程包括主线程都关联了一个RunLoop对象。除了主线程的RunLoop在程序启动时被开启,其他线程的RunLoop都需要手动开启。

待解决的疑问:

3.2. RunLoop事件处理流程

弄清楚RunLoop内部处理逻辑是理解RunLoop的关键,将单独写一篇博客进行分析。

待解决的疑问:

以上两方面,将在下一篇博客Cocoa RunLoop 系列之源码解析中结合源代码来找到答案。

4. 执行反馈

RunLoop Observers机制属于RunLoop一个反馈机制,将RunLoop一次循环划分成若干个节点,当执行到对应的节点调用相应的回调函数,将RunLoop当前的执行状态反馈给用户。

三、何时使用RunLoop

由于主线程的RunLoop在程序启动时被自动创建并执行,因此只有在其他线程中才需要手动启动RunLoop。很多情况下,对于RunLoop的使用多数情况是在主线程中,包括进行RunLoop模式切换,设置RunLoop Observer等。

在非主线程中,以下几种情况适用于RunLoop:

四、总结

一直以来,RunLoop对我来说都属于一个比较模糊的概念,在实际编程中也有用到RunLoop的一些功能,确实感觉到很强大,但是仅仅停留在应用层面,并不是很理解具体含义。因此,为了更好的使用RunLoop,有必要研究和梳理RunLoop相关的知识点。

上一篇 下一篇

猜你喜欢

热点阅读