什么是NSRunLoop

什么是Runloop?

RunLoop是一个事件处理循环,是一种让线程能随时处理事件但不退出的机制。它的目的是为了让线程在有消息到来时能够被立即唤醒执行任务,在没有消息使能够使线程休眠避免资源占用。
一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。Event Loop模型提供了一个机制,让线程能随时处理事件但并不退出,他的代码逻辑大概是这样的:

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

NSRunLoop是对Event Loop模型的一种具体实现。

运行循环从两种不同类型的源接收事件。输入源提供异步事件,通常是来自另一个线程或来自不同应用程序的消息。定时器源提供在指定时间执行或指定时间间隔重复执行的同步事件。两种类型的源都使用特定于应用程序的处理程序例程来处理事件。

Run Loop Modes

一个run loop mode就是input sources(Port-Based Sources、Custom Input Sources、Cocoa Perform Selector Sources)、Timers和该run loop所有的监听者(observers)的集合。每次执行run loop都需要指定一个mode。不同mode通过名称区分。
Cocoa和Core Foundation都定义了默认模式和几种常用模式,以及用于在代码中指定这些模式的字符串。您只需为模式名称指定自定义字符串即可定义自定义模式。
Foundation框架常用的mode有以下几种:

  1. NSDefaultRunLoopMode
    会经常使用,默认模式是用于大多数操作的模式。大多数情况下,您应该使用此模式启动运行循环并配置输入源。

  2. NSEventTrackingRunLoopMode
    使用此模式在鼠标拖动循环和其他种类的用户界面跟踪循环期间限制传入事件。

  3. NSModalPanelRunLoopMode
    A run loop should be set to this mode when waiting for input from a modal panel, such as NSSavePanel or NSOpenPanel.

  4. UITrackingRunLoopMode
    The mode set while tracking in controls takes place. You can use this mode to add timers that fire during tracking.
    在控件中触发用户界面跟踪时使用该模式. 可以用该模式添加需要在用户界面跟踪起价执行的timer.

  5. NSRunLoopCommonModes
    会经常使用,这是一组可配置的常用模式。将输入源与此模式相关联也会将其与组中的每个模式相关联。也就是说,关联到这个模式的事件或输入源会自动关联到这个组下面其他的模式。
    对于Foundation应用程序,此集合默认包括NSDefaultRunLoopMode,NSModalPanelRunLoopMode和NSEventTrackingRunLoopMode。UITrackingRunLoopMode在Threading Programming Guide中没有说明,但是在NSRunLoopMode的Reference中有说明,不确定是否包含。
    这也就是为什么,当我们需要添加一个可以在UIScrollView滑动时也可以执行的timer时,需要将timer添加到NSRunLoopCommonModes的RunLoop。因为当我们滑动UIScrollView时,RunLoop会切换到NSEventTrackingRunLoopMode执行,将timer添加到NSRunLoopCommonModes模式的RunLoop时,会自动把这个timer添加到这个组下面其他的模式。

Input Sources

输入源一般有两种,一种是基于端口的输入源,监听应用程序的Mach端口传递的事件,操作系统内核会自动发送信号。一种是自定义输入源,监听自定义源的事件,需要从其他线程手动发送信号。

Port-Based Sources

Cocoa和Core Foundation框架对使用端口对象和函数创建基于端口的输入源提供了内建支持。你不需要直接去创建输入源,而是通过创建一个NSPort对象并使用NSPort对象的方法将端口添加到run loop。NSPort对象会帮你处理输入源需要的创建和配置工作。

Custom Input Sources

自定义输入源,除了要定义事件到达之后的行为,还需要自己定义事件传递机制。

Cocoa Perform Selector Sources

除了基于端口的源,Cocoa还定义了一个自定义输入源用于在任何线程执行选择器。与基于端口的源不同,执行选择器源会在执行完他的选择器之后把自身从run loop中移除。
当你在其他线程执行一个选择器时,目标线程必须有一个活动的run loop,这对于你自己创建的线程来说,意味着你必须在代码里面显示的开启了run loop之后才会执行选择器。添加到主线程的选择器会被立即执行,是因为应用程序启动时主线程已经默认创了一个活动的run loop。

下表是定义在NSObject对象中可以在线程中执行/取消执行选择器的方法:
Timer Sources

定时器源传递同步事件,用于通知线程在指定的时间去处理响应的事情。定时器需要跟指定的mode关联,如果定时器没有在当前正在执行的mode中,那么它并不会被触发。定时器源并不是实时的,它可能存在延迟。

Run Loop Observers

run loop会在执行过程中的特定时间想观察者发送消息。这些时间点包括:

  • The entrance to the run loop.
  • When the run loop is about to process a timer.
  • When the run loop is about to process an input source.
  • When the run loop is about to go to sleep.
  • When the run loop has woken up, but before it has processed the event that woke it up.
  • The exit from the run loop.
    可以使用Core Foundation框架中的CFRunLoopObserverRef对象向run loop注册观察者。

RunLoop与线程

1.每条线程都有唯一的与之对应的RunLoop对象。
2.主线程的RunLoop已经创建好了,而子线程的需要手动创建。(也就是说子线程的RunLoop默认是关闭的,因为有时候开了个线程但却没有必要开一个RunLoop。 )
3.RunLoop在第一次获取时创建,在线程结束时销毁。

References & Acknowledgements

官方文档
深入理解RunLoop

你可能感兴趣的:(什么是NSRunLoop)