RunLoop介绍和使用

上次讲了runtime,这次是runloop,虽然两者都是run开头的名词术语,但是在OC中,这两个东西压根没啥联系。这篇文章主要讲讲runloop的一些概念和用法。其中包含:

  • 什么runloop
  • runloop是怎么存在的
  • runloop中包含哪些东西
  • 日常开发中使用到runloop 的场景

一、什么是runloop

一个很容易想到的现象: 当我们将手机解锁进入某个APP之后,如果不操作手机(包括网络请求的行为),手机不会有任何反应,一旦我们进行了操作的时候,手机就会执行响应的动作。那当我们不操作的时候手机在做什么呢?  如果对受的状态进行检测的话,会发现CPU的使用率几乎是零,也就是说,没有进行操作的时候的手机的状态是休眠的! 专业看来说,在手机没有接受事件的时候,手机自动进入休眠状态,但是保持一种随时可唤醒的姿势,等待用户的事件传入。这种机制在很多不同的系统中都有,被称之为事件循环。在苹果中,实现这一机制的就是RunLoop了。

我们大概把runloop想象成一个do-while循环,在不接受退出的指令的状态,会一直执行循环下去。而runloop在这基础之上加入了更复杂的逻辑,使得runloop不仅可以在没有事件输入的情况下进行休眠,同时做到随时随地的响应事件。

逻辑上可以理解成下面的代码。

func loop() {
do {
var message = get_next_message();
process_message(message); //处理信息
} while (message != quit);
}

当然,实际上比这个要复杂的多。至少我不觉的这个循环里我能做一些精确的控制。让我们先看看runloop中还包含了其他的一些的东西。

先看看NSRunLoop。NSRunLoop是基于CF RunLoopRef的封装,但两者并不是 toll-free briged,在OC中,可以使用下面的方式获取。

[aRunLoop  getCFRunLoop];

看看苹果对NSRunloop的介绍说明:

The NSRunLoop class declares the programmatic interface to objects that manage input sources. An NSRunLoop object processes input for sources such as mouse and keyboard events from the window system, NSPortobjects, and NSConnection objects. An NSRunLoop object also processes NSTimer events.

NSRunLoop管理程序的输入事件,这些事件包含了来自窗口鼠标和键盘的事件,以及NSport端口事件和网络请求事件,同时还包括了timer事件。也即所有的事件,都会经过runloop的管理,在APP中有序的执行。

二、runloop是怎么存在的

在苹果的NSRunloop 的文件中我们看到这些:

FOUNDATION_EXPORT NSRunLoopMode const NSDefaultRunLoopMode;
FOUNDATION_EXPORT NSRunLoopMode const NSRunLoopCommonModes @property (class, readonly, strong) NSRunLoop *currentRunLoop;
@property (class, readonly, strong) NSRunLoop *mainRunLoop NS_AVAILABLE(10_5, 2_0);
#endif @property (nullable, readonly, copy) NSRunLoopMode currentMode; - (CFRunLoopRef)getCFRunLoop CF_RETURNS_NOT_RETAINED; - (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode; - (void)addPort:(NSPort *)aPort forMode:(NSRunLoopMode)mode;
- (void)removePort:(NSPort *)aPort forMode:(NSRunLoopMode)mode; - (nullable NSDate *)limitDateForMode:(NSRunLoopMode)mode;
- (void)acceptInputForMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate; @end @interface NSRunLoop (NSRunLoopConveniences) - (void)run;
- (void)runUntilDate:(NSDate *)limitDate;
- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate; #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
- (void)configureAsServer NS_DEPRECATED(10_0, 10_5, 2_0, 2_0);
#endif /// Schedules the execution of a block on the target run loop in given modes.
/// - parameter: modes An array of input modes for which the block may be executed.
/// - parameter: block The block to execute
- (void)performInModes:(NSArray<NSRunLoopMode> *)modes block:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); /// Schedules the execution of a block on the target run loop.
/// - parameter: block The block to execute
- (void)performBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

这其中,我发现它并不能通过某个方式创建。正常的Object对象,一般都可以通过 init 来创建实力对象。然而,NSRunLoop并没有。但是我们可以看到NSRunLoop有一个属性:

@property (class, readonly, strong) NSRunLoop *currentRunLoop;

事实上,NSRunLoop不能直接创建,只能通过currentRunLoop来获取。并且每个RunLoop只能在当前线程中操作,因为runloop不是线程安全的。看过喵神的技术博客的应该知道,NSRunLoop是基于CFRunloop的封装,而CFRunloop是纯C的API,它是线程安全的。这里一直提到了线程,其实是想说明,NSRunLoop依赖于线程,并且每个线程最多只有一个NSRunLoop,对于主线程,在APP启动时候,main函数中就已经开启了一个NSRunLoop用于服务主线程,这个runloop可以通过  NSRunLoop 的 mainRunLoop获得。如果是CFRunLoop ,则可以通过CFRunLoopGetMain() 和 CFRunLoopGetCurrent()获取当前线程的runloop和主线程的runloop。

线程和runloop一一对应,当线程被销毁的时候,runloop也GG了。所以,要维系一个runloop,首先要保证线程的存状态。其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)。

三、runloop中包含哪些东西

runloop管理了线程中的事件,这些事件包含:timer事件、source事件以及Observer。还包含了事件的运行的model-RunLoopMode。

timer事件即是我们使用的定时器。

source事件包含两类,一类是source0,另一类是source1,两者的区别是:

• Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
• Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程。

Observer是观察者,它可以监听runloop的运行状态,可以监听以下状态:

    kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
    kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
    kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
    kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
    kCFRunLoopExit          = (1UL << 7), // 即将退出Loop

RunLoopMode:这是runloop的一种标记。它有几种形式:

1. kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。
2. UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
3. UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
4: GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。
5: kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。

上面提到的timer、source、Observer都不是直接放在runloop中的,而是作为item的形式被标记为某个model之中,然后runloop运行在对应的model时,才会管理这些item。当我们要处理某个item的时候,首先要确保item处于runloop对应的mode之中。

四、日常开发中使用到runloop 的场景

runloop在日常的开发中,使用的场景是比较少的。我们经常会有这么一种情况,如果使用定时器执行的一个小动画,在拖动scrollView的时候会被暂停。这是因为,runloop通常情况在kCFRunLoopDefaultMode中运行,也即说,直接创建的NStimer将在这个model中。在拖动scrollView的时候,runloop就切换到了UITrackingRunLoopMode下,之前在kCFRunLoopDefaultMode中的item不再被runloop维护,也就停止了运行。要决解这个,只需要通过

- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode;

将NStimer加入到UITrackingRunLoopMode中即可。 还有一种办法,在所有的mode中,还有一种kCFRunLoopCommonModes,它是的作用类似于它的名字,时公共的mode,也即是在这个mode中的item,会被所有的mode支持。 我们可以将NStimer标记为Commonmode达到同样的目的。

还有其他的类似的情况之后,我们还能在著名的AFNetWorking中看到使用了Runloop :

+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}

这样开启了一个线程,同时开启runloop,并添加了一个port事件维系runloop 的运行,但是port并不发送时机的消息。这个线程是AFNetWorking用于将NSURLConection置于后台处理请求和回调的。

Y神的文章:http://blog.ibireme.com/2015/05/18/runloop/

iOS RunLoop了解和使用的更多相关文章

  1. iOS runloop 资源汇总-b

    RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利 ...

  2. iOS Runloop理解

    一.RunLoop的定义 当有持续的异步任务需求时,我们会创建一个独立的生命周期可控的线程.RunLoop就是控制线程生命周期并接收事件进行处理的机制. RunLoop是iOS事件响应与任务处理最核心 ...

  3. iOS RunLoop详解

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

  4. iOS runLoop 理解

    目录 概述 run loop modes 一.概述 run loop叫事件处理循环,就是循环地接受各种各样的事件.run loop是oc用来管理线程里异步事件的工具.一个线程通过run loop可以监 ...

  5. iOS Runloop 消息循环

    介绍 Runloop是一种事件监听循环,可以理解成一个while死循环,监听到事件就起来,没有就休息. Runloop可以在不同模式下进行切换,iOS有五种模式,其中UIInitializationR ...

  6. ios runloop学习

    今天突然才之间才意识到NSTimer这样的运行方式,是在多线程中实现的循环还是在主线程中去实现的呢.当然不可能是在主线程中的while那么简单,那样什么都干不了,简单看了下NSTimer是以同步方式运 ...

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

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

  8. iOS RunLoop简介

    一.什么是RunLoop? RunLoop是运行循环,每个Cocoa应用程序都由一个处于阻塞状态的do/while循环驱动,当有事件发生时,就把事件分派给合适的监听器,如此反复直到循环停止.处理分派的 ...

  9. IOS RunLoop面试题

    一 什么是RunLoop? 从字面意思看就是运行循环,其实内部就是do-while循环,这个循环内部不断地处理各种任务(比 如Source,Timer,Observer) 一个线程对应一个RunLoo ...

随机推荐

  1. 芯片SA58672(功放芯片)

    1::下面的中文图可能不准: 针对上面的图,数据手册中有一些参数的推导:      这个式子是电压增益的   这个式子是关于截止频率的  典型原理图: 需要电源去耦,能够提高效率. PVDD引脚处的电 ...

  2. Javascript和jquery事件--事件对象event

    1.  事件对象event 对于event,js的解释是Event 对象代表事件的状态,比如事件在其中发生的元素.键盘按键的状态.鼠标的位置.鼠标按钮的状态.而jq的解释是事件处理(事件对象.目标元素 ...

  3. OpenJDK源码研究笔记(十六):在Java中使用JavaScript脚本语言

    友情提示 本文主要参考了51CTO上的一篇文章,代码经过自己的模仿和整理,还算凑合. 本文中的代码注释比较多,不再过多解释. 更多用法,还是得看JDK的API或者看原文http://developer ...

  4. Android ServiceManager启动

    许久就想写篇关于servicemanager的文章,之前对服务启动顺序诸如zygote,systemserver.等启动顺序理解有点混乱,现做例如以下理解分析: 事实上init进程启动后,Servic ...

  5. Oracle空间数据库的备份与恢复

    大型GIS系统,存储.管理海量(TB级)空间数据时,数据库备份变的尤其重要.这里随笔说说冷备份的一种方法. 基于ArcSDE.Oracle空间库的冷备份: (1) 在数据入库工作后或者更新变动较大时, ...

  6. C语言编程程序的内存怎样布局

    在c语言中,每一个变量和函数有两个属性:数据类型和数据的存储类别. C语言中局部变量和全局变量变量的存储类别(static,extern,auto,register) 1. 从变量的作用域划分变量(即 ...

  7. nslookup详解(name server lookup)( 域名查询)

    nslookup详解(name server lookup)( 域名查询) 一.总结 1.爬虫倒是很方便拿到页面数据:a.网页的页面源码我们可以轻松获得  b.比如cnsd博客,文章的正文内容全部放在 ...

  8. 关于JavaScript概念的总结

    原文 https://www.jianshu.com/p/1e8d8a691aa8 大纲 1.JavaScript的概念 2.JavaScript 特点 3.JavaScript是弱类型语言 4.Ja ...

  9. 【42.59%】【codeforces 602A】Two Bases

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  10. js中ajax连接服务器open函数的另外两个默认参数get请求和默认异步(open的post方式send函数带参数)(post请求和get请求区别:get:快、简单 post:安全,量大,不缓存)(服务器同步和异步区别:同步:等待服务器响应当中浏览器不能做别的事情)(ajax和jquery一起用的)

    js中ajax连接服务器open函数的另外两个默认参数get请求和默认异步(open的post方式send函数带参数)(post请求和get请求区别:get:快.简单 post:安全,量大,不缓存)( ...