RunLoop 的使用相关

使用 RunLoop

我们应该只在创建辅助线程的时候,才显示的运行一个 RunLoop.iOS app 会在应用启动的时候帮我 run 一个 runloop,而我们自己新建的辅助线程不会.
对于辅助线程,我们仍然需要判断是否需要启动一个 RunLoop.比如我们使用一个线程去处理一个预先定义的长时间的任务,我们应该避免启动 RunLoop.下面是官方document 提供的使用 RunLoop 的几个场景:

需要使用 Port-Based Input Source或者 Custom InputSource 和其他thread通讯时
需要在线程中使用 Timer
需要在线程中使用上文中提到的selector相关的方法
需要让线程周期性的执行某些工作

如果你选择使用 RunLoop, runloop 的设置和启动是比较直观的. 同时,你需要实现什么情况下从辅助线程中退出 runloop, 最好不要直接关闭线程,而是先退出 runloop.

如何创建和设置 runloop.代码:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW5

RunLoop 对系为增加 input source,timers,添加 observers 提供了主要的接口.每一个 thread 都有且仅有一个 runloop.Cocoa 中使用 NSRunLoop, CoreFoundation 中使用 CFRunLoopRef.

从线程中获取 RunLoop 对象

为了从当前 thread 中获取runloop 对象,具体步骤如下:

在 Cocoa中, 使用 [NSRunLoop currentRunLoop] ,就会返回当前线程的 runLoop 对象.
CoreFoundation 中使用 CFRunLoopRef.

CFRunLoopRef和 NSRunLoop 可以转化, NSRunLoop 使用getCFRunLoop方法就可以得到 CFRunLoopRef 对象

配置 RunLoop 对象

在辅助线程启动 runloop 之前,你必须至少在其中添加一个 input source 或者 timer.如果一个 runloop 中没有一个事件源sources, runloop 会在你启动它以后立即退出.

在添加了 source 以后,你可以给 runloop 添加 observers 来监测 runloop 的不同的执行的状态.为了加入 observer, 你应该创建一个 CFRunLoopObserverRef,使用 CFRunLoopAddObserver 函数添加 observer 到你的 runloop.

下面代码块显示了, 如何给 RunLoop 添加一个 observer .

- (void)threadMain
{
    // The application uses garbage collection, so no autorelease pool is needed.
    NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];

    // Create a run loop observer and attach it to the run loop.
    CFRunLoopObserverContext  context = {0, self, NULL, NULL, NULL};
    CFRunLoopObserverRef    observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
            kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);

    if (observer)
    {
        CFRunLoopRef    cfLoop = [myRunLoop getCFRunLoop];
        CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
    }

    // Create and schedule the timer.
    [NSTimer scheduledTimerWithTimeInterval:0.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.
        [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
        loopCount--;
    }
    while (loopCount);
}

启动 RunLoop

在辅助线程中,启动一个 runloop 是必须的.runloop 中必须有一个 input source 或者 timer事件源.如果 runloop 启动时候,内部没有监听任何一个 input source 或者 timer, runloop 会立即 exit.

有以下几种启动 RunLoop 的方法:

1.没有设置特定的条件启动

2.设置一个时间限制

3.在一个特定的 mode

最简单的是一个无条件的启动 runloop,但是这也是最差的选择.如果没有设置任何条件,就会将 runloop 所在的 thread 进行永久的事件循环.你可以增加或者减少 input sources, timers,但是只有一种方法能够 kill 掉它.同时这种方式没办法让 runloop 在自定义的 mode 中运行.

替代无条件进入run loop更好的办法是用预设超时时间来运行runloop,这样runloop运作直到某一事件到达或者规定的时间已经到期。如果是事件到达,消息会被传递给相应的处理程序来处理,然后runloop退出。你可以重新启动runloop来等待下一事件。如果是规定时间到期了,你只需简单的重启runloop或使用此段时间来做任何的其他工作。

除了超时机制,你也可以使用特定的模式来运行你的runloop。模式和超时不是互斥的,他们可以在启动runloop的时候同时使用。模式限制了可以传递事件给run loop的输入源的类型,这在”Run Loop模式”部分介绍。

描述了线程的主要例程的架构。本示例的关键是说明了runloop的基本结构。本质上讲你添加自己的输入源或定时器到runloop里面,然后重复的调用一个程序来启动runloop。每次runloop返回的时候,你需要检查是否有使线程退出的条件成立。示例中使用了Core Foundation的run loop例程,以便可以检查返回结果从而确定run loop为何退出。若是在Cocoa程序,你也可以使用NSRunLoop 的方法运行run loop,无需检查返回值。

- (void)threadMain
{
    // The application uses garbage collection, so no autorelease pool is needed.
    NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];

    // Create a run loop observer and attach it to the run loop.
    CFRunLoopObserverContext  context = {0, self, NULL, NULL, NULL};
    CFRunLoopObserverRef    observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
            kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);

    if (observer)
    {
        CFRunLoopRef    cfLoop = [myRunLoop getCFRunLoop];
        CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
    }

    // Create and schedule the timer.
    [NSTimer scheduledTimerWithTimeInterval:0.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.
        [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
        loopCount--;
    }
    while (loopCount);
}

可以递归的运行run loop。换句话说你可以使用CFRunLoopRun,CFRunLoopRunInMode或者任一NSRunLoop的方法在输入源或定时器的处理程序里面启动run loop。这样做的话,你可以使用任何模式启动嵌套的run loop,包括被外层run loop使用的模式。

退出 RunLoop

有两种方法可以让 runloop 退出:

  1. 给 runloop 设置超时事件

2.通知 runloop 停止

如果可以配置的话,推荐使用第一种方法。指定一个超时时间可以使run loop退出前完成所有正常操作,包括发送消息给run loop观察者。

使用CFRunLoopStop来显式的停止runloop和使用超时时间产生的结果相似。Runloop把所有剩余的通知发送出去再退出。与设置超时的不同的是你可以在无条件启动的run loop里面使用该技术。

尽管移除runloop的输入源和定时器也可能导致run loop退出,但这并不是可靠的退出run loop的方法。一些系统例程会添加输入源到runloop里面来处理所需事件。因为你的代码未必会考虑到这些输入源,这样可能导致你无法没从系统例程中移除它们,从而导致退出runloop。

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

推荐阅读更多精彩内容