RunLoop

作者: 静持大师 | 来源:发表于2016-09-02 23:50 被阅读105次
    • 什么是RunLoop

      • 运行循环
      • 一个线程对应一个RunLoop,主线程的RunLoop默认已经启动,子线程的RunLoop需手动启动(调用run方法)
      • RunLoop只能选择一个Mode启动,如果当前Mode中没有任何Source(Sources0、Sources1)、Timer,那么就直接退出RunLoop
      • 用来在线程上管理事件异步到达的基础设施,runLoop为 线程监测一个或多个事件源
    • RunLoop作用

      • 保持程序的持续运行
      • 处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)
      • 节省CPU资源,提高程序性能:该做事时做事,该休息时休息(重要)
      • 调用解耦(类似详细队列Message Queue)
    • 模拟RunLoop内部实现

      • 其实它内部就是do-while循环,在这个循环内部不断地处理各种任务(比如Source、Timer、Observer)

    • 获得RunLoop对象
      • RunLoop对象
        • NSRunLoop (Foundation框架)
        • CFRunLoopRef (Core Foundation框架)
      • Foundation
    [NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
    [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
    
    + Core Foundation
    
    CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
    CFRunLoopGetMain(); // 获得主线程的RunLoop对象
    
    • RunLoop结构
      • CFRunLoopRef对应RunLoop对象
    RunLoop结构
    • CFRunLoopModeRef代表RunLoop的运行模式, 系统默认注册了5个Mode
      • NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行
      • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
      • NSRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
    • CFRunLoopTimerRef是基于时间的触发器
      • CFRunLoopTimerRef基本上说的就是NSTimer、CADisplayLink等,它受RunLoop的Mode影响
    • CFRunLoopSourceRef是事件源(输入源)
      • Source是RunLoop的数据源抽象类(protocol)
        • Sourece0:处理App内部事件、App自己负责管理(触发),如UIEvent
        • Sourece1:由RunLoop和内核管理,Mach port驱动,如CFMachPort等
    • CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变,框架中很多机制都都RunLoopObserver触发,如CAAnimation
    RunLoop状态改变
    // 1.创建Observer
    // 第一个参数:用于分配该observer对象的内存
    // 第二个参数:用以设置该observer所要关注的的事件
    // 第三个参数:用于标识该observer是在第一次进入run loop时执行, 还是每次进入run loop处理时均执行
    // 第四个参数:用于设置该observer的优先级
    // 第五个参数: observer监听到事件时的回调block
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch(activity)
        {
            case kCFRunLoopEntry:
                NSLog(@"即将进入loop");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"即将处理timers");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"即将处理sources");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"即将进入休眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"刚从休眠中唤醒");
                break;
            case kCFRunLoopExit:
                NSLog(@"即将退出loop");
                break;
            default:
                break;
        }
    });
    
    // 2.添加监听
    /*
     第一个参数: 给哪个RunLoop添加监听
     第二个参数: 需要添加的Observer对象
     第三个参数: 在哪种模式下监听
     */
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);
    
    // 3,释放observer
    CFRelease(observer);
    
    • RunLoop处理逻辑(事件队列).
      每次运行 run loop,你线程的 run loop 对会自动处理之前未处理的消息,并通
      知相关的观察者。具体的顺序如下:
      • 1.通知观察者run loop 已经启动,即将进入runloop
      • 2.通知观察者任何即将要开始的定时器
      • 3.通知观察者任何即将启动的非基于端口的源
      • 4.启动任何准备好的非基于端口的源
      • 5.如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9
      • 6.通知观察者线程进入休眠
      • 7.将线程置于休眠直到任一下面的事件发生:
        • 7.1 某一事件到达基于端口的源、定时器启动、run loop 被显式唤醒
      • 8.通知观察者线程将被唤醒
      • 9.处理未处理的事件
        • 9.1 如果用户定义的定时器启动,处理定时器事件并重启 run loop。进入步骤 2
        • 9.2 如果输入源启动,传递相应的消息
        • 9.3 如果 run loop 被显式唤醒而且时间还没超时,重启 run loop。进入步骤 2
      • 10.通知观察者 run loop 结束
    RunLoop处理逻辑

    .png)


    • RunLoopRunLoop应用
    • NSTimer
      • 只能在指定的model下运行
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    
    • ImageView显示
      • 只能在指定的model下设置图片
    • PerformSelector
      • 只能在指定的model下调用
    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:[UIImage imageNamed:@"lnj"] waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];
    
    • 常驻线程
      • 必须调用run才会执行死循环
      • NSRunLoop的model中必须有source/timer,死循环才不会退出
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    [runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    [runloop run]
    
    • 自动释放池
    activities = 0x1 = 1
    1: 即将进入RunLoop : 创建一个自动释放池
    activities = 0xa0 = 160 = 128 + 32
    32:即将休眠 : 释放上一次的自动释放池, 创建一个新的自动释放池
    128:即将退出RunLoop : 释放自动释放池
    

    1.NSRunLoop的实现机制,及在多线程中如何使用
    NSRunLoop是IOS消息机制的处理模式
    >1.NSRunLoop的主要作用:控制NSRunLoop里面线程的执行和休眠,在有事情做的时候使当前NSRunLoop控制的线程工作,没有事情做让当前NSRunLoop的控制的线程休眠。
    >2.NSRunLoop 就是一直在循环检测,从线程start到线程end,检测inputsource(如点击,双击等操作)异步事件,检测timesource同步事件,检测到输入源会执行处理函数,首先会产生通知,corefunction向线程添加runloop observers来监听事件,意在监听事件发生时来做处理。
    >3.runloopmode是一个集合,包括监听:事件源,定时器,以及需通知的runloop observers
    
    >1. 只有在为你的程序创建次线程的时候,才需要运行run loop。对于程序的主线程而言,run loop是关键部分。Cocoa提供了运行主线程run loop的代码同时也会自动运行run loop。IOS程序UIApplication中的run方法在程序正常启动的时候就会启动run loop。如果你使用xcode提供的模板创建的程序,那你永远不需要自己去启动run loop
    >2. 在多线程中,你需要判断是否需要run loop。如果需要run loop,那么你要负责配置run loop并启动。你不需要在任何情况下都去启动run loop。比如,你使用线程去处理一个预先定义好的耗时极长的任务时,你就可以毋需启动run loop。Run loop只在你要和线程有交互时才需要
    
    

    面试题:请谈一谈runloop

    一条线程对应一个runloop,而主线程只要一启动会默认绑定一个runloop.那系统内部我查看过源码,他是通过字典的形式把我的线程和我的这个runloop绑定在一起.子线程的runloop默认情况下是没有的,如果想使用子线程的runloop,只需要调用currentrunloop就可以,我们可以把runloop理解为一个懒加载的形式,只有用到的时候才会创建.只要调用currentrunloop,他就会根据字典key去内存中取,如果没有,就会创建一个新的runloop,和我们当前的子线程绑定在一起,并且保存到字典当中,runloop和线程是一个一对一的关系.

    每个runloop内有很多mode模式,每一个model里面又有很多的source、timer、observer,runloop只能执行一个模式,如果执行A模式,那么其他模式下的source、timer、observer,无效,只有A模式有效.(为什么要这样做?)防止多个模式下的source、timer、observer,紊乱,不好控制逻辑

    timer 的执行流程?通过timer来监听执行的流程1、进入runloop,首先会处理系统的事件,处理完系统的source,timer后runloop会进入休眠状态.
    只要我们触发一些事件以后,runloop会醒来,就会处理timer,source

    runloop什么时候死掉?1、创建timer时,会给timer创建一个默认的生命周期,我们也可以自定义生命周期 2、线程挂掉

    相关文章

      网友评论

          本文标题:RunLoop

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