Runloop学习

Runloop学习

| 目录 |
|: ------------- |
| 1 什么是Runloop? |
| 2 进一步了解Runloop|
| 3 Runloop示例一 |
| 4 Runloop示例二 |

1 什么是Runloop?

一个Runloop就是一个处理事件的循环,通过Runloop可以让线程在没有任务时处于睡眠状态,当有任务触发时,也可以立即处理。这里,从一个简单的例子出发,看看Runloop的使用。

//按钮1,以睡眠的方式保证线程持续运行
- (IBAction)buttonNormalThreadTestPressed:(UIButton *)sender {
    NSLog(@"EnterbuttonNormalThreadTestPressed");

    threadProcess1Finished =NO;
    [NSThread detachNewThreadSelector:@selector(threadProce)
                             toTarget:self
                           withObject:nil];

    while (!threadProcessFinished) {
        [NSThread sleepForTimeInterval: 0.5];
    }

    NSLog(@"ExitbuttonNormalThreadTestPressed");
}

//按钮2,以Runloop的方式保证线程持续运行
- (IBAction)buttonRunloopPressed:(id)sender {
    NSLog(@"Enter buttonRunloopPressed");

    threadProcess2Finished =NO;
    [NSThread detachNewThreadSelector:@selector(threadProce)
                             toTarget:self
                           withObject:nil];

    while (!threadProcessFinished) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                 beforeDate:[NSDate distantFuture]];
    }

    NSLog(@"Exit buttonRunloopPressed");
}

//按钮3,测试按钮
- (IBAction)buttonTestPressed:(id)sender{
    NSLog(@"Enter buttonTestPressed");
    NSLog(@"Exit buttonTestPressed");
}

//线程函数
BOOL threadProcessFinished =NO;
-(void)threadProce{
    NSLog(@"Enter threadProce.");
    threadProcessFinished =NO;
    for (int i=0; i<5;i++) {
        NSLog(@"InthreadProce count = %d.", i); sleep(1);
    }
    threadProcessFinished =YES;
    NSLog(@"Exit threadProce.");
}

操作一:点击按钮1,然后立即点击按钮3,发现:只有线程结束后,才响应按钮3的事件。具体结果如下:

NSRunloopDemo[47515:645301] EnterbuttonNormalThreadTestPressed NSRunloopDemo[47515:645594] Enter threadProce. NSRunloopDemo[47515:645594] InthreadProce count = 0. NSRunloopDemo[47515:645594] InthreadProce count = 1. NSRunloopDemo[47515:645594] InthreadProce count = 2. NSRunloopDemo[47515:645594] InthreadProce count = 3. NSRunloopDemo[47515:645594] InthreadProce count = 4. NSRunloopDemo[47515:645594] Exit threadProce. NSRunloopDemo[47515:645301] ExitbuttonNormalThreadTestPressed NSRunloopDemo[47515:645301] Enter buttonTestPressed NSRunloopDemo[47515:645301] Exit buttonTestPressed

操作二:点击按钮2,然后立即点击按钮3,发现:在Runlopp等待过程才可以响应按钮3的事件。具体结果如下:

NSRunloopDemo[47748:651304] Enter buttonRunloopPressed NSRunloopDemo[47748:651523] Enter threadProce. NSRunloopDemo[47748:651523] InthreadProce count = 0. NSRunloopDemo[47748:651304] Enter buttonTestPressed NSRunloopDemo[47748:651304] Exit buttonTestPressed NSRunloopDemo[47748:651523] InthreadProce count = 1. NSRunloopDemo[47748:651523] InthreadProce count = 2. NSRunloopDemo[47748:651523] InthreadProce count = 3. NSRunloopDemo[47748:651523] InthreadProce count = 4. NSRunloopDemo[47748:651523] Exit threadProce.

2 进一步了解Runloop

通过上面的例子,我们对Runloop有了一个基本的认识,下面我们进一步的了解Runloop。

2.1 任务来源:Runloop接受来自 输入源定时源 两个来源的任务。

  1. 输入源:投递异步消息,通常来自于另一个thread或另一个应用程序。
  2. 定时源:在计划的时间或重复的时间间隔内投递同步消息。

2.2 Runloop对外接口

Runloop包含5个类:CFRunLoopRef、CFRunLoopModeRef、CFRunLoopSourceRef、CFRunLoopTimerRef、CFRunLoopObserverRef。每个Runloop包含多个Mode,每个Mode由若干个Source、Timer、Observer组成。调用Runloop的主函数时,需要指定一个Mode作为CurrentMode。

2.3 Runloop内部逻辑:当有任务触发时,Runloop会自动处理之前未处理的消息,并通知相关的观察者。

  1. 通知观察者:Runloop已经启动。

  2. 通知观察者:即将开始处理Timer。

  3. 通知观察者:即将启动Source。

  4. 启动Source。

  5. 如果Source准备好并处于等待状态,立即启动并进入步骤 9。

  6. 通知观察者:线程即将进入休眠。

  7. 将线程置于休眠直到任一下面的事件发生:

    a. 某一事件到达基于端口的源;
    b. 定时器启动;
    c. Runloop设置的时间已经超时;
    d. Runloop被显式唤醒。

  8. 通知观察者:线程即将被唤醒。

  9. 处理未处理的事件

    a. 如果用户定义的定时器启动,处理定时器事件并重启 Runloop。进入步骤 2。
    b. 如果输入源启动,传递相应的消息。
    c. 如果Runloop被显式唤醒而且时间还没超时,重启 Run loop,进入步骤 2。

  10. 通知观察者Run loop结束。

3 Runloop示例一

如下示例中,Runloop的输入源是一个NSTimer类型的Source,NSTimer每隔一秒给Runloop触发一次任务,由Runloop处理。

- (void)viewDidLoad
{
    [super viewDidLoad];

    [NSThread detachNewThreadSelector:@selector(newThreadProcess)
                             toTarget:self
                           withObject:nil];
}

- (void)newThreadProcess
{
    @autoreleasepool {
        //获得当前thread的Runloop
        NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
    
        //设置Run loop observer的运行环境
        CFRunLoopObserverContext context = {0,(__bridge void *)(self),NULL,NULL,NULL};
    
        //创建Run loop observer对象
        CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
        if(observer)
        {
            //将Cocoa的NSRunLoop类型转换成CoreFoundation的CFRunLoopRef类型
            CFRunLoopRef cfRunLoop = [myRunLoop getCFRunLoop];
            //将新建的observer加入到当前thread的runloop
            CFRunLoopAddObserver(cfRunLoop, observer, kCFRunLoopDefaultMode);
        }
    
        [NSTimer scheduledTimerWithTimeInterval: 1
                                        target:self
                                      selector:@selector(timerProcess)
                                      userInfo:nil
                                       repeats:YES];
        NSInteger loopCount = 2;
        do{
            //启动当前thread的loop
            [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:12.0]];
            loopCount--;
        }while (loopCount);
    }
}

void myRunLoopObserver(CFRunLoopObserverRef observer,CFRunLoopActivity activity,void *info)
{
    switch (activity) {
        case kCFRunLoopEntry:
            NSLog(@"run loop entry"); break;
        case kCFRunLoopBeforeTimers:
            NSLog(@"run loop before timers");  break;
        case kCFRunLoopBeforeSources:
            NSLog(@"run loop before sources"); break;
        case kCFRunLoopBeforeWaiting:
            NSLog(@"run loop before waiting"); break;
        case kCFRunLoopAfterWaiting:
            NSLog(@"run loop after waiting");  break;
        case kCFRunLoopExit:
            NSLog(@"run loop exit"); break;
        default:
            break;
    }
}

- (void)timerProcess{
    for (int i=0; i<5; i++) {       
        NSLog(@"In timerProcess count = %d.", i);
        sleep(1);
    }
}

4 Runloop示例二

阻塞线程,在其他线程执行后再执行。

BOOL StopFlag =NO;
- (void)viewDidLoad
{
    [super viewDidLoad];
    StopFlag =NO;

    [NSThread detachNewThreadSelector:@selector(newThreadProc)
                         toTarget:self
                       withObject:nil];
    while (!StopFlag) {
        NSLog(@"Beginrunloop");
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                             beforeDate:[NSDate distantFuture]];
        NSLog(@"Endrunloop.");
    }
    NSLog(@"OK");
}

//方案一:等待新的线程执行完毕
//结果:在新线程执行完成后很久,Runloop才收到退出的消息
-(void)newThreadProc{
    NSLog(@"Enter newThreadProc.");

    for (int i=0; i<10; i++) {
        NSLog(@"InnewThreadProc count = %d.", i);
        sleep(1);
    }

    StopFlag =YES;
    NSLog(@"Exit newThreadProc.");
}

//方案二:在新线程中执行完成后,通知主线程
//结果:在新线程执行完成后,Runloop直接收到退出的消息
-(void)newThreadProc{
    NSLog(@"Enter newThreadProc.");

    for (int i=0; i<10; i++) {
        NSLog(@"InnewThreadProc count = %d.", i);
        sleep(1);
    }

    [self performSelectorOnMainThread:@selector(setEnd)
                       withObject:nil
                    waitUntilDone: NO];
    NSLog(@"Exit newThreadProc.");
}

-(void)setEnd{
    StopFlag = YES;
}

方案二相比于方案一,更体现出使用Runloop的目的:有任务时执行任务,没有任务时休眠。

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

推荐阅读更多精彩内容

  • 先贴下 apple doc, 本文基本是对照 doc 的翻译:https://developer.apple.co...
    brownfeng阅读 6,855评论 8 111
  • 什么是Run Loops RunLoops是与线程相关联的基础部分,一个Run Loop就是事件处理循环,他是用来...
    傻傻小萝卜阅读 961评论 0 5
  • Runloop是iOS和OSX开发中非常基础的一个概念,从概念开始学习。 RunLoop的概念 -般说,一个线程一...
    小猫仔阅读 991评论 0 1
  • 什么是Runloop · 一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让...
    806349745123阅读 299评论 0 1
  • 我相信能熬过异地是真爱, 我不敢说我真的爱倪, 因为在此之前我还什么都给不了倪, 但我想说这份感情我真的是用心经营...
    细嗅恋人阅读 199评论 0 0