美文网首页
十二、RunLoop详解

十二、RunLoop详解

作者: LNG61 | 来源:发表于2018-08-03 15:48 被阅读0次

一、什么是RunLoop

先来看下RunLoopCFRunLoopRun函数的实现:

void CFRunLoopRun(void){
  int32_t result;
  do{
    result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
  } while(kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result)
}

开启RunLoop之后会调用CFRunLoopRun函数,该函数使用do{}while()循环不断处理CFRunLoopRunSpecific,直到RunLoop结束或者需要停止才退出。
RunLoop接收到消息时(用户输入、程序执行)会处理消息,空闲时休眠,是一种Event loop的模型。RunLoop开启时,一直处于接受消息-等待-处理的循环中。这里我们着重介绍下CFRunLoop,至于NSRunLoop,其实就是对CFRunLoop的一层封装。

二、RunLoopMode

RunLoopMode就是RunLoop的相关模式,其定义如下:

struct __CFRunLoopMode{
  CFRuntimeBase _base;
  pthread_mutex_t _lock; // 锁
  CFStringRef _name; // 模式名字
  Boolean _stopped; // 是否停止
  char _padding[3];
  CFMutableSetRef _sources0; // source0
  CFMutableSetRef _sources1;  // source1
  CFMutableArrayRef _observers; // observers
  CFMutableArrayRef _timers; // timers
  
  // ... other
};

常用的Mode有以下几个:

Mode 说明
kCFRunLoopDefaultMode 默认运行方式
UITrackingRunLoopMode 使用这个Mode去跟踪来自用户交互的事件
GSEventReceiveRunLoopMode 用来接受系统事件,内部的Run Loop Mode
kCFRunLoopCommomModes 这是一个伪模式,其为一组run loop mode的集合
NSConnectionReplyMode 使用这个Mode去监听NSConnection对象的状态,我们很少需要自己使用这个Mode

三、RunLoopSource

struct __CFRunLoopSource{
  CFRuntimeBase _base;
  uint32_t bits;
  pthread_mutex_t _lock;
  CFIndex _order;
  CFMutableBagRef _runLoops;
  union{
    CFRunLoopSourceContext version0;
    CFRunLoopSourceContext1 version1;
  } _context;
};

source有两个版本:version0version1,分别对应CFRunLoopModesource0source1
source0需要进行标记为可操作,然后再唤醒(wakeup)runloop进行处理。
source1是基于mach_port的,用于跟其它线程互相发送消息,通信。

四、RunLoopTimer

CFRunLoopTimer是定时器,包含时间和回调函数指针,时间点到时,RunLoop会被唤醒并执行该定时器。

五、RunLoopObserver

struct __CFRunLoopObserver{
  CFRuntimeBase _base;
  pthread_mutex_t _lock;
  CFRunLoopRef _runLoop;
  CFIndex _rlCount;
  CFOptionFlags _activities;
  CFIndex _order;
  CFRunLoopObserverCallBack _callout;
  CFRunLoopObserverContext _context;
};

CFRunLoopObserverRef是一个观察者,当RunLoop的状态改变时,会通过_callout回调。状态有以下几种:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity){
  kCFRunLoopEntry = (1UL << 0), // 即将进入Loop
  kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
  kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
  kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
  kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
  kCFRunLoopExit = (1UL << 7), // 即将退出Loop
  kCFRunLoopAllActivities = 0x0FFFFFFFU
}

六、RunLoop工作流程

首先来看下简化版的CFRunLoopRunSpecific函数:

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled){
  // 即将进入Loop
  __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
  result = __CFRunLoop(rl, currentMode, seconds, returnAfterSourceHandled, previousMode){
    // 即将处理 Timer
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopBeforeTimers);
    // 即将处理 Source
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopBeforeSources );

    // source0
    __CFRunLoopDoBlocks(rl, rlm);
    BOOLean sourceHandledThisStop = __CFRunLoopDoSource0(rl, rlm, stopAfterHandle);
    if(sourceHandledThisStop){
      //如果有source0需要处理的话,sourceHandledThisStop为true
      __CFRunLoopDoBlocks(rl, rlm);
    }

    if(MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime){
      // 检查livePort是否有待处理的消息,如果有则goto handle_msg,否则进入休眠等待唤醒
      msg = (mach_msg_header_t *)msg_buffer;
      if(__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)){
            goto handle_msg;
        }
    }
    
    // 即将进入休眠
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopBeforeWaiting);
    
    // 监听端口,等待消息唤醒
    do{
        msg = (mach_msg_header_t *)msg_buffer;
        // TIMEOUT_INFINITY无限等待
        __CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
        if(modeQueuePort != MACH_PROT_NULL && modeQueuePort == modeQueuePort){
          while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
          if(rlm->_timerFired){
            // 处理timer
            rlm->_timerFired = false;
            break;
          }else{
            // free msg
          }
        }else{
            // 处理其它消息
            break;
        }
    } while(1);

    // 刚从休眠中唤醒
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopAfterWaiting );
     
    handle_msg:
    // 根据livePort处理timer, source1等
    if(MACH_PORT_NULL == livePort){
    }else if(livePort == rl->_wakeUpPort){
      // wakeUp
    }else if(rlm->_timerPort == livePort){
      __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
    }else if(livePort == dispatchPort){
      __CFRUNLOOP_IS_SERVICINT_THE_MAIN_DISPATCH_QUEUE__(msg);
    }else{
       // source1
        __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply);
    }

    // 改变状态,如果retVal不为0,则代表需要结束RunLoop了
    if(sourceHandledThisLoop && stopAfterHandle){
        retVal = kCFRunLoopRunHandledSource;
    }else if(timeout_context->termTSR < mach_absolute_time()){
        retVal = kCFRunLoopRunTimedOut;
    }else if(_CFRunLoopIsStopped(rl)){
        retVal = kCFRunLoopRunStopped;
    }else if(rlm->_stopped){
        rlm->_stopped = false;
        retVal = kCFRunLoopRunStopped;
    }else if(_CFRunLoopModeIsEmpty(rl, rlm, previousMode)){
        retVal = kCFRunLoopRunFinished;
    }
  } while(0 == retVal)

  // 7. 即将退出Loop
  __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit );
  return result;
}

RunLoop流程主要如下:

  1. 即将进入Loop
  2. 将要处理Timer
  3. 将要处理Source0
  4. 处理Source0
  5. 如果有Source1或Timer待处理,则跳转到第步
  6. 通知即将休眠
  7. 进入休眠,等待唤醒
  8. 通知被唤醒
  9. 处理Source1和Timer
  10. 如果RunLoop仍需处理其它消息,则跳转到2
  11. 即将推出Loop

相关文章

  • iOS Runtime

    iOS RunLoop详解---重要而详细iOS RunLoop详解-部分

  • 十二、RunLoop详解

    一、什么是RunLoop 先来看下RunLoop中CFRunLoopRun函数的实现: 开启RunLoop之后会调...

  • RunLoop详解

    RunLoop详解 RunLoop运行循环(死循环) RunLoop模式 NSDefaultRunLoopMode...

  • Runloop 详解

    Runloop 详解 参考链接: 深入理解RunLoop CFRunLoop 概念 runloop :是管理和处理...

  • Runloop

    Runloop 实现原理及应用iOS - RunLoop 底层源码详解及具体运用

  • 深入理解RunLoop

    深入理解runloopiOS RunLoop详解

  • 记录一些介绍Runloop的牛文

    老司机出品——源码解析之RunLoop详解深入理解RunLoop关于RunLoop部分源码的注释CFRunLoop...

  • 底层原理探究(二)RunLoop

    转自: 老司机出品——源码解析之RunLoop详解入门使用: RunLoop入门 看我就够了孙源的Runloop视...

  • RunLoop 详解

    关于 runloop 的运行 点击链接详解附有源码

  • iOS RunLoop 详解

    转自 iOS RunLoop 详解 image.png Runloop 是和线程紧密相关的基础组件,是很多多线程有...

网友评论

      本文标题:十二、RunLoop详解

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