Runloop学习

作者: 不多满 | 来源:发表于2016-06-24 10:33 被阅读66次

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的目的:有任务时执行任务,没有任务时休眠。

相关文章

  • IOS runloop 学习笔记

    这次学习 的内容是 runloop 1.runloop 是什么2.runloop 的作用3.runloop 和 线...

  • RunLoop学习资料

    非常好的runloop学习系列 CoreFoundation源码 RunLoop系列之源码分析 关于Runloop...

  • NSRunLoop

    前言 RunLoop的初期学习总结,后续会持续研究更新。 一、Runloop定义及作用 1. 什么是Runloop...

  • RunLoop学习总结

    通过以下文章学习记录 关于Runloop的原理探究及基本使用 深入理解RunLoop RunLoop完全指南 Ru...

  • RunLoop-基础概念(初识篇)

    学习这篇内容主要讲解RunLoop的概念,以及RunLoop和线程之间的关系。当然提及RunLoop也离不开Aut...

  • Runloop学习

    Runloop学习 | 目录 ||: ------------- || 1 什么是...

  • RunLoop学习

    RunLoop概念 人如其名,RunLoooooooooooooooop,像是一个死循环,不停的跑圈,永不懈怠。除...

  • Runloop学习

    深入理解RunLoop | Garan no dou

  • RunLoop学习

    读这篇Blog:https://blog.ibireme.com/2015/05/18/runloop/ 收货比较...

  • Runloop学习

    苹果官方的简介: The programmatic interface to objects that manag...

网友评论

    本文标题:Runloop学习

    本文链接:https://www.haomeiwen.com/subject/abyodttx.html