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

    相关文章

      网友评论

        本文标题:Runloop学习

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