RunLoop

作者: superWX | 来源:发表于2015-08-28 18:33 被阅读309次

    RunLoop

    RunLoop就是运行循环,处理app中的各种事件(比如触摸事件,定时器事件,Selector事件)
    一个线程对应一个RunLoop,主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建,RunLoop在第一次获取时创建,线程结束时销毁

    RunLoop的获得

    • Foundation
      • [NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
      • [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
    • Core Foundation
      • CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
      • CFRunLoopGetMain(); // 获得主线程的RunLoop对象

    RunLoop相关类

    • Core Foundation中关于RunLoop的5个类
      • CFRunLoopRef
      • CFRunLoopModeRef
      • CFRunLoopSourceRef
      • CFRunLoopTimerRef
      • CFRunLoopObserverRef

    RunLoop的组成

    • Mode
      • Timer
      • source
      • Observer

    Mode

    Mode代表RunLoop的运行模式。每次RunLoop职能指定一个Mode运行。切换Mode职能退出Loop,再重新指定一个Mode进入。
    一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer

    系统默认的5个Mode

    • NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行

    • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

    • UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用

    • GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到

    • NSRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode(既执行Default中的事件也执行track中的事件)

    Timer

    CFRunLoopTimerRef是基于时间的触发器

    CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode影响

    GCD的定时器不受RunLoop的Mode影响

    - (void)timer
    {
        NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
        // 定时器只运行在NSDefaultRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作(比如textField的拖动)
        //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        
        // 定时器只运行在UITrackingRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作
        //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
        
        // 定时器会跑在标记为common modes的模式下
        // 标记为common modes的模式:UITrackingRunLoopMode和NSDefaultRunLoopMode
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    }
    
    - (void)timer2
    {
        // 调用了scheduledTimer返回的定时器,已经自动被添加到当前runLoop中,而且是NSDefaultRunLoopMode
        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
        
        // 修改模式
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    }
    
    - (void)run
    {
        NSLog(@"----run");
    }
    
    

    Source

    • CFRunLoopSourceRef是事件源(输入源)

    • 按照官方文档,Source的分类

      • Port-Based Sources (跟其他线程交互,或通过内核发送的消息)
      • Custom Input Sources (自动以输入源,几乎不用)
      • Cocoa Perform Selector Sources (比如performSelecter等方法)
    • 按照函数调用栈,Source的分类

      • Source0:非基于Port的
      • Source1:基于Port的,通过内核和其他线程通信,接收、分发系统事件

    事件的传入都是先传入Source1。然后再由Source1分发至source0进行处理。所以在函数调用栈中往往只看到Source0。

    Obersver

    CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变

    • 可以监听的时间点
    /* Run Loop Observer Activities */
    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry = (1UL << 0), // 1
        kCFRunLoopBeforeTimers = (1UL << 1), // 2
        kCFRunLoopBeforeSources = (1UL << 2), // 4
        kCFRunLoopBeforeWaiting = (1UL << 5), // 32
        kCFRunLoopAfterWaiting = (1UL << 6), // 64
        kCFRunLoopExit = (1UL << 7), // 128
        kCFRunLoopAllActivities = 0x0FFFFFFFU
    };
    
    • 添加Observer
    - (void)observer
    {
        // 创建observer
        CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            NSLog(@"----监听到RunLoop状态发生改变---%zd", activity);
        });
    
        // 添加观察者:监听RunLoop的状态
        CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
        
        // 释放Observer
        //凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release
        //比如CFRunLoopObserverCreate
        //release函数:CFRelease(对象);
        
        CFRelease(observer);
    }
    

    RunLoop处理逻辑

    • 1:通知观察者run loop已经启动
    • 2:通知观察者任何即将要开始的定时器
    • 3:通知观察者任何即将启动的source0
    • 4:启动任何准备好的source0
    • 5:如果接收到source1准备好并处于等待状态,立即启动,并进入步骤9
    • 6:通知观察者线程进入休眠
    • 7:将线程置于休眠直到任何一下面的事件发生:
      • 某一事件到达source1
      • 定时器启动
      • runloop设置的时间超时
      • runloop被显式唤醒
    • 8:通知观察者线程将被唤醒
    • 9:处理未处理的事件
      • 如果用户定义的定时器启动,处理定时器事件并重启runloop,进入步骤2
      • 如果输入源启动,传递相应的消息
      • 如果runloop被显示唤醒而且时间还未超时,重启runloop,进入步骤2
    • 10:通知观察者runloop结束

    相关文章

      网友评论

        本文标题:RunLoop

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