读 Run Loops Document 笔记

下面主要是对Run Loops官方文档的翻译及总结。

定义

run loop 是一个事件处理的循环,负责对工作进行调度,同时协调接收即将到来的任务。他的目的是确保线程在有任务的时候立即工作,没有任务的时候就歇着。
run loop 的管理不是完全自动的,你可以通过代码去运行线程的run loop,让他去处理特定的任务。包括主线程在内的所有线程,都不需要自己去创建run loop,他们本身内部就有runloop对象,只不过主线程中的runloop是在程序启动的时候就自动运行起来了,而主线程外的其他线程的runloop 需要用户自己去run。

剖析

run loop 就像它的名字一样,它是一个在线程内部的循环,用来运行那些负责处理即将进入线程的事件的程序。
run loop接收来自于两种类型数据源的事件;一种是Input source,负责传递异步事件,主要来自其他线程或者其他的应用程序;另外一种是Timer source 负责传递同步事件,主要来计划时间触发或者是重复间隔触发事件。当两种源类型的事件到达后,都用特定的程序来进行处理。

image.png

上图是run loop及其数据源的构想结构图,input source 将异步事件传递个对应的处理程序,最终调用runUntilDate:方法退出run loop,Timer将事件传递给对应的处理程序,但不会导致run loop 退出。
除了处理数据源的输入外,run loop还会为一些特性产生通知,只要注了run loop的观察者都可以接受到这些通知,并且在线程内做一些额外的工作。

  • Run Loop Modes

run loop mode 是被监控的input source和 timer source的集合,也是需被run loop通知的观察者的集合。每当你运行你的run loop 的时候,你都需要间接或直接的指定一个在内部运行的mode 。在run loop 运行的过程中,只监控与当前mode相关联的source,也只接受关联source传递的事件。而与其他mode相关联的source及传递的事件,只能等待到对应的mode才能被处理,因此你可以通过mode来过滤出不需要的数据源传递的事件。
系统默认提供了几种mode,你也可以自定义mode,当你自定义mode时,一定要在mode内添加一个或多个的 input source或者timer source 或者观察者以确保该mode有用。

  • 系统预定义mode

    • 1:NSDefaultRunLoopMode 默认mode,多数情况下run loop在这种mode下运行。

    • 2:NSConnectionReplyMode

    • 3:NSModalPanelRunLoopMode

    • 4:NSEventTrackingRunLoopMode 页面的滑动等运行在这用mode下

    • 5: NSRunLoopCommonModes 这是一个可配置的通用mode组,将input source 和这个mode相关联,同时也会与组内的其他mode相关联,在Cocoa applications中,这个组中默认包括default, modal, event tracking modes,你也可以通过CFRunLoopAddCommonMode(::)方法将自定义的mode加入到这个组中。

  • input source

    input source 以异步方式传递事件给线程。而基于input source 的事件源主要分为两类: Port-based input sources:监控你应用程序的Match端口;Custom input sources:监控自定义的事件源。他们两个对于系统来说没有任何差别,唯一的差别是是如何发出信号,Port-based是由系统的内核自动发出信号,Custom input 需要从其他线程手动的发送信号。

    当你创建一个input source 的时候,你可将他分配给你的run loop 的一个或多个mode,mode会随时影响到哪个input source被监控,如果你的input source 没有在当前被监控的mode中,那么事件需要等到runloop 执行对应的mode时才会被执行(这就是如果你将轮播图的定时器加入在default mode中后,如果滑动页面时,轮播图不会动的原因,滑动页面时当前runloop运行的是track mode,解决办法是将定时器加入到common mode中,或者将定时器加入到新的线程,并且run该线程的runloop)。

  • Port-Based Sources、Custom Input Sources
    详见
    Configuring a Port-Based Input Source.
    Defining a Custom Input Source
    除上面外,系统定义了一个custom source 允许用户调用,Cocoa Perform Selector Sources,就是performSelectorOnMainThread:withObject:waitUntilDone:
    等方法

  • Timer source

Timer source 是预先设置好的,在未来的某个时间向线程传递同步事件,是一种线程提醒自己做某事的方式。
Timer source 的产生虽然是基于时间的通知,但是他并不是实时的,和input source一样,它也需要和runloop的具体mode相关联。如果与之关联的mode没有被监控,那么timer将拥有不被触发。
详见Configuring Timer Sources

  • Run Loop Observers

和需要适当的同步或异步事件来触发的事件源不同,Run Loop Observers是由runloop自己在运行到对应的的位置的时去候触发的。用户可以由此根据不同的时机去做一些相应的处理。主要的可以关联的属性如下:

  • 进入runloop
  • runloop即将处理timer
  • runloop即将处理input source
  • runloop即将进入睡眠
  • runloop被唤醒,在处理唤醒事件前
  • 从runloop中退出
    详细创建Configuring the Run Loop.
  • The Run Loop Sequence of Events

每次运行runloop它处理待处理事件的整个过程如下:

  • 1.通知已经进入runloop
  • 2.通知即将处理准备好的timer
  • 3.通知即将处理所有非port based 的input source事件
  • 4.处理那些待处理的非port based 的input source事件
  • 5.如果有在等待的基于port based 的input source事件则立即去处理,然后到第9步骤
  • 6.通知runloop即将进入睡眠
  • 7.保持睡眠状态,直到发生任何一个如下事件:
    • 有机遇port based 的input source事件到达
    • fire一个timer
    • 为runloop设置的超时值到期
    • runloop被明确唤醒
  • 8.通知线程被唤醒
    1. 处理待处理的事件
    • 如果用户定义的timer被触发,处理timer事件,重启runloop,跳到第2步骤
    • 如果input source 被触发,则传递事件
    • 如果runloop是被明确唤醒的且还没有超时,则重新运行runloop,跳转到第2步骤

When Would You Use a Run Loop?

主线程的runloop是在整个应用开始运行的时候,由系统自动启动的,所以需要用户手动启动的只有其他的辅助线程,而在辅助线程中也要根据情况去考虑,是否有启动runloop的必要,runloop的预期是你需要和线程有更多的交互。
如果在下面的任意情况之一,那么就需要去启动runloop:

  • 通过port或者custom input source 和其他线程进行交流
  • 在线程内应用timer
  • 应用任何的 performSelector..方法
  • 保持线程以执行定期任务

Using Run Loop Objects

  • 获得runloop对象

  //获得当前线程的runloop对象
  NSRunLoop *runloop = [NSRunLoop currentRunLoop];
  • 配置runloop

当你运行runloop前一定要先像其中加入至少一个input source 或者 timer,否则的话只要一运行,runloop就会停止。除了source 外,你也可以向其注册观察者,用 CFRunLoopObserverRef和`CFRunLoopAddObserver方法去创建与添加。
下面的代码展示了添加观察者的过程

  
void myfunc() {
    NSLog(@"回调");
};

- (void)viewDidLoad {
    [super viewDidLoad];
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    
    CFRunLoopObserverContext  context = {0, (__bridge void *)(self), NULL, NULL, NULL};
    CFRunLoopObserverRef    observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &myfunc, &context);
 
    if (observer)
    {
        CFRunLoopRef    cfLoop = [runloop getCFRunLoop];
        CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
    }

    [NSTimer scheduledTimerWithTimeInterval:1 target:self
                                   selector:@selector(doFireTimer) userInfo:nil repeats:YES];
   
    NSInteger    loopCount = 10;
    do
    {
        // Run the run loop 10 times to let the timer fire.
        [runloop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
        loopCount--;
    }
    while (loopCount);
}

- (void)doFireTimer {
    NSLog(@"timer fun");
}

为长期存在的线程配置runloop时,最好至少添加一个input source来接收消息。 虽然您可以附加一个定时器进入运行循环,但一旦定时器触发,它通常会失效,这会导致运行循环退出。 附加重复计时器可以使运行循环运行更长的时间,但是会涉及定期触发计时器以唤醒您的线程,这实际上是另一种形式的轮询。 相比之下,input source会等待事件到来的过程中,让线程保持睡眠状态。

Configuring Run Loop Sources

大牛的文章从源码实现的角度介绍runloop,也可以去观摩相互印证,加深理解。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,548评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,497评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,990评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,618评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,618评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,246评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,819评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,725评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,268评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,356评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,488评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,181评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,862评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,331评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,445评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,897评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,500评论 2 359

推荐阅读更多精彩内容

  • 来或者去,是为了什么,可有想明白? 忙或者闲,都无所谓,只是值不值得? 匆匆的人生,有什么短长可言? 精彩、传奇、...
    晓露轻尘阅读 302评论 0 2
  • 连载目录 | 琵琶行传奇 上一章:琵琶行传奇之网红生涯 | 连载二 文 / 十三明月 “诶,你的ID名可真够绕口的...
    十三明月阅读 540评论 0 3
  • 有时 云很淡 心欢心 也有时 云痕长 心意凉 有时 雨微醺 知长久 有时 雨倾盆 一瞬间 些许清凉
    亦之游阅读 64评论 1 2