RunLoop详解

  • 2019 年 10 月 8 日
  • 筆記

一、CFRunLoop部分源码

阅读源码:CFRunLoop.c

CFRunLoopRef CFRunLoopGetCurrent(void) {      CHECK_FOR_FORK();      CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);      if (rl) return rl;      return _CFRunLoopGet0(pthread_self());  }
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {    ...    //从字典获取runloop    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));    __CFUnlock(&loopsLock);    // 如果没获取到    if (!loop) {       //创建新的runloop      CFRunLoopRef newLoop = __CFRunLoopCreate(t);      __CFLock(&loopsLock);        // 从字典里再找      loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));      if (!loop) {            //如果还没有 就把新的存进去          CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);            // 新创建的赋值给loop          loop = newLoop;      }            // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it      __CFUnlock(&loopsLock);      CFRelease(newLoop);    }    ...  }

从源码中可以看出

一个线程对应一个RunLoop

RunLoop保存在一个全局的字典里 线程为key RunLoop作为Value

线程刚创建的时候没有Ru nLoop对象,RunLoop会在第一次获取它的时候创建

主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop 只有调用[NSRunLoop currentRunLoop] 才创建runLoop

由于key是线程 所以在线程结束的时候RunLoop也会随之销毁

二 Runtime相关的类

1、Core Fuoundation中愿意RunLoop有5个类

CFRunLoopRef
CFRunLoopModelRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

2、CFRunLoop

struct __CFRunLoop {      CFRuntimeBase _base;      pthread_mutex_t _lock;			/* locked for accessing mode list */      __CFPort _wakeUpPort;			// used for CFRunLoopWakeUp      Boolean _unused;      volatile _per_run_data *_perRunData;              // reset for runs of the run loop      pthread_t _pthread;      uint32_t _winthread;      CFMutableSetRef _commonModes;      CFMutableSetRef _commonModeItems;      CFRunLoopModeRef _currentMode;      CFMutableSetRef _modes;      struct _block_item *_blocks_head;      struct _block_item *_blocks_tail;      CFAbsoluteTime _runTime;      CFAbsoluteTime _sleepTime;      CFTypeRef _counterpart;  };

重点看下面几个

struct __CFRunLoop {     // 线程对象      pthread_t _pthread;      CFMutableSetRef _commonModes;      CFMutableSetRef _commonModeItems;      CFRunLoopModeRef _currentMode; //当前模式CFRunLoopMode      CFMutableSetRef _modes; // CFRunLoopModes  }

看一下CFRunLoopModeRef

typedef struct __CFRunLoopMode *CFRunLoopModeRef;    struct __CFRunLoopMode {   ...      char _padding[3];      CFMutableSetRef _sources0;  //CFRunLoopSource      CFMutableSetRef _sources1;  //CFRunLoopSource      CFMutableArrayRef _observers;  //CFRunLoopObserver      CFMutableArrayRef _timers; //CFRunLoopTimer  ...  };

3、RunLoop结构

Sources0

触摸事件处理

performSelector:OnThread:

Source1

基于Port的线程间通信

系统事件的捕捉(source1 捕捉 source0处理)

Timers

NSTimer

performSelector:withObject:afterDealy:

observer

用于监听RunLoop的状态

UI刷新(BeforeWaiting)

AutoRelease pool

observer 监听类型
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {      kCFRunLoopEntry = (1UL << 0), // 进入runloop      kCFRunLoopBeforeTimers = (1UL << 1), // 即将进入timers      kCFRunLoopBeforeSources = (1UL << 2), // 即将进入source      kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠      kCFRunLoopAfterWaiting = (1UL << 6), //刚从个休眠中唤醒      kCFRunLoopExit = (1UL << 7), //即将推出Loop      kCFRunLoopAllActivities = 0x0FFFFFFFU  };
  • 如果model里没有任何source0/source1/observers/timers 会退出runloop
  • CFRunLoopMode
    1. CFRunLoopDefaultMode 默认
    2. UITrackingRunLoopMode 用于界面跟踪Mode用于scrollview追踪触摸滑动,保证界面滑动不受其他的mode影响
    3. CFRunLoopCommonModes

4、RunLoop运行逻辑

通过在打印调用栈 找到CFRunLoopRunSpecific

我们查看源码 看一下RunLoop的运行逻辑

/* rl, rlm are locked on entrance and exit */  static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {        //如果有timer 处理timer   ->kCFRunLoopBeforeTimers      if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);      //如果有Source 处理Source - >kCFRunLoopBeforeSources      if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);     // 处理Block  __CFRunLoopDoBlocks(rl, rlm);      //处理Source0  如果Source有block 处理Source0的block ->__CFRunLoopDoSources0      Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);      if (sourceHandledThisLoop) {          __CFRunLoopDoBlocks(rl, rlm);  }        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {          msg = (mach_msg_header_t *)msg_buffer;          // 处理Source1中的事件 port  __CFRunLoopServiceMachPort 如果有事件  goto-> handle_msg          if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {              goto handle_msg;          }        }      //即将休眠 __CFRunLoopSetSleeping  	if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);  	__CFRunLoopSetSleeping(rl);            do {                //等待唤醒              __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);                if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {                  // Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.                  while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));                  if (rlm->_timerFired) {                      // Leave livePort as the queue port, and service timers below                      rlm->_timerFired = false;                      break;                  } else {                      if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);                  }              } else {                  // Go ahead and leave the inner loop.                  break;              }          } while (1);            // 被唤醒后 调用  即将唤醒  	__CFRunLoopUnsetSleeping(rl);          //继续监听  	if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);              handle_msg:;           if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {              CFRUNLOOP_WAKEUP_FOR_TIMER();              // 如果有timer 处理timer; ->__CFRunLoopDoTimers              if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {                  // Re-arm the next timer, because we apparently fired early                  __CFArmNextTimerInMode(rlm, rl);              }          }  else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {              CFRUNLOOP_WAKEUP_FOR_TIMER();              // 如果有timer 处理timer; ->__CFRunLoopDoTimers                if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {                  __CFArmNextTimerInMode(rlm, rl);              }          }          // GCD唤醒          else if (livePort == dispatchPort) {              // ansy to MainQueue  同步到主线程              __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);            } else {              //处理Source1  		sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;                  // Restore the previous voucher              _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, previousVoucher, os_release);          }        //处理block  	__CFRunLoopDoBlocks(rl, rlm);            //设置返回值  	if (sourceHandledThisLoop && stopAfterHandle) {  	    retVal = kCFRunLoopRunHandledSource;          } else if (timeout_context->termTSR < mach_absolute_time()) {              retVal = kCFRunLoopRunTimedOut;  	} else if (__CFRunLoopIsStopped(rl)) {              __CFRunLoopUnsetStopped(rl);  	    retVal = kCFRunLoopRunStopped;  	} else if (rlm->_stopped) {  	    rlm->_stopped = false;  	    retVal = kCFRunLoopRunStopped;  	} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {  	    retVal = kCFRunLoopRunFinished;  	}        return retVal;  }

mach_msg()直接睡眠 内核层面

  • [ NSRunLoop currentRunLoop]run ] 是无法停止的 是一个永不销毁的线程。//可以看方法注释
  • CFRunloopRunInMode 第三个参数是 执行完source后是否退出
  • 结构体初始化最好赋值为{0} 避免内存垃圾