Run Loops基础概念篇二

When Would You Use a Run Loop?

你唯一要使用run loop,就是当你要在application中创建线程的时候。你Application的主线程是架构很重要的一部分。所以,iOS系统为app的提供了runloop代码,并自动开始。在iOS中运行Main loop作为app启动步骤的一部分。

对于其他的线程,你需要考虑是否必须要使用run loop。如果有需要,配置并自己启动它。你不需要在每个线程中都启用run loop。例如,如果你使用一个线程来执行一些很长时间的运行和已经准备好的任务,你可能要避免使用它。Run loop的目的主要是用来解决线程间的通信。例如,如果你打算做下面的事情,你需要开启一个run loop。

  • 使用port或者自定义input source来和其他线程通讯
  • 在线程中使用timer
  • 在Cocoa application使用performSelector…方法
  • 维持线程来执行周期性的任务

如果你选择使用run loop,配置和设置就是接下来要做的。纵观整个线程编码过程,你应该有一个明确的认识在什么时候退出线程,这比强制退出好很多。关于如何配置和退出run loop的信息在这Using Run Loop Objects

Using Run Loop Objects

run loop对象提供了主要的接口来添加input sources,timers,run-loop observers到run loop中再运行它。每个线程有一个和它相关的run loop object。在Cocoa中,这个对象是NSRunLoop的实例。在应用的底层,它是CFRunLoopRef类的指针。

Getting a Run Loop Object

获取当前线程的runloop对象,你使用下面的一种方式:

尽管它们不是无缝桥接的类,当需要的时候你可以从NSRunLoop对象获得一个CFRunLoopRef指针。NSRunLoop类定义了一个getCFRunLoop方法,它返回了一个CFRunLoopRef类型,这你可以传入到Core Foundation中。因为两个对象指向同一个run loop,需要的时候你可以混合使用。

Configuring the Run Loop

当你在自定义线程中跑run loop的时候,你至少添加一个input source或timer到run loop中,如果一个run loop没有任何source来监听,当你尝试运行它的时候,它会立即退出。如何向run loop中添加一个source,请看这里Configuring Run Loop Sources

除了装载sources,你也可以装载run loop observers,使用它们来监听run loop的执行步骤。为了装载run loop observer,你创建了一个CFRunLoopObserverRef指针,并且使用CFRunLoopAddObserver方法并把它添加到你的run loop中。Run loop observer必须要使用Core Foundation来创建。

下面展示了在一个线程中,添加一个run loop observer到run loop中的主要的代码部分。

- (void)threadMain
{
    //The application use garbage collection,so no autorelease pool is needed.
    NSRunLoop *myRunLoop = [NSRunLoop currentRunLoop];  
    //Create a run loop observer and attach it to the run loop.
    CFRunLoopObserverContent 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的时候,至少添加一个input source到接受的消息。尽管你能够只需要通过一个timer来启动run loop中,但是,一旦timer触发之后,它就会无效的,这会引起run loop的退出。传入一个repeat的timer可以保持run loop运行很长一段时间。但是将会周期性的调用触发的timer来唤醒你的线程。这是维持线程中run loop的另一种方式,一个input source等待事件的到达,让你的线程处于睡眠中,直到它被唤醒。

Starting the Run Loop

一个run loop必须装载至少一个input source或者timer来。如果没有,run loop立即退出。
下面有几种办法开启一个run loop。

  • 无条件的启动run loop
  • 设置一个超时时间启动run loop
  • 通过一种特殊的模式启动run loop

无条件的启动runloop是最简单的,但是这也是最不推荐的一种方式。无条件的启动你的runloop,把线程作为runloop的一个参数。这种方式对runloop的控制权很小。你可以添加或移除input source和timers,唯一停止runloop的方式就是kill。这种启动方式没有办法在自定义mode。

除了无条件的启动一个runloop,使用time out value来开始一个runloop更好。当你使用time out value,runloop运行直到事件到达或者超时。如果事件抵达,事件会被派发到一个handler来处理,然后run loop退出。你的code又能够重新开始runloop去处理下一个事件。如果超时,你可以简单的重启runloop,或者利用这段时间做任何需要的事。

除了time out value,你也可以使用一个指定的mode来运行runloop。Modes和timeout value并不是互斥的。当运行runloop的时候也可以同时使用它们。Modes限制了source的type。详细被描述在Run Loop Modes

下面的code主要部分展示了runloop的基本结构。本质上,你添加你的input source和timer到runloop。重复调用其中一个routine来开始一个run loop。每次run loop routine 返回的时,你检查run lop是否满足某些条件导致退出thread。例子中使用Core Foundation的run loop routines,以至于它能检查返回的结果并检测run loop为什么退出。你也能使用NSRunLoop的方法以一种相似的方式来运行runloop,如果你正在使用Cocoa框架中的NSRunLoop,它是不需要检查return value的。

- (void)skeletonThreadMain {
    //Set up an autorelease pool here if not using garbage collection.
    BOOL done = NO;
    //Add your sources or timer to the run loop and do any other setup
    do
    {
        //Start the run loop but return after earch source is handled
        SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,10,YES)
        
        //If a source explicitly stopped the run loop,or if there no sources or timers,go head and exit.
        if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished))
        done = YES;
        
        //Check for any other exit conditions here and set the done variable as needed
    }
    while(!done);
    //Clean up code here. Be sure to release any allocated autorelease pools.
}

递归运行run loop也是可能的。换句话说,你能够调用CFRunLoopRunCFRunLoopRunInMode,任何NSRunLoop的方法,从input source 或者 timer的handler routine开始runloop。当这样做的时候,你可以使用你想要的mode来运行内嵌的runloop。包括被outer run loop正在使用的mode。

Exiting the Run Loop

有两种办法使runloop在处理事件之前退出。

  • Runloop配置一个time out时间
  • 主动告诉Runloop停止

使用time out时间是一种比较好的选择。在退出之前,指定一个time out时间让runloop完成所有的任务,包括向runloop observer发送通知。

使用CFRunLoopStop方法停止一个runloop,产生一个类似于超时的结果。使用runloop发送完剩下的runloop通知然后退出。唯一不同的就是只有当你无条件的启动Runloop的时候才能使用这种方式退出。

尽管移除runloop的input source和timers可以引起runloop的退出,但是这不是一种稳定的方式。一些系统一般添加input source到run loop来处理需要的事件。因为你的code不需要关心这些input source。所以你不能移除input sources,让run loop退出。

Thread Safety and Run Loop Objects

线程安全取决于你使用操作runloop的API。在Core Foundation中的方法一般是线程安全的,可以被任何其他线程调用。如果你正在执行操作,这些操作改变了runloop的配置。无论什么时候,这样做任然是一个很好的实践。

Cocoa的NSRunLoop类。Cocoa的NSRunLoop和Core Foundation不一样,不是线程安全的。如果你使用NSRunLoop类来修改你的runloop,你应该在原来的线程中做这些操作。在其他线程添加一个input source或者timer到原来runloop可能引起crash,或者一个意想不到的行为。

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

推荐阅读更多精彩内容