前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RunLoop详解

RunLoop详解

作者头像
老沙
发布2019-09-28 13:20:35
6220
发布2019-09-28 13:20:35
举报
文章被收录于专栏:老沙课堂

一、CFRunLoop部分源码

阅读源码:CFRunLoop.c

代码语言:javascript
复制
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}
代码语言:javascript
复制
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
代码语言:javascript
复制
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;
};

重点看下面几个

代码语言:javascript
复制
struct __CFRunLoop {
   // 线程对象
    pthread_t _pthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode; //当前模式CFRunLoopMode
    CFMutableSetRef _modes; // CFRunLoopModes
}

看一下CFRunLoopModeRef

代码语言:javascript
复制
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 监听类型
代码语言:javascript
复制
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的运行逻辑

代码语言:javascript
复制
/* 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} 避免内存垃圾

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老沙说点事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、CFRunLoop部分源码
    • 从源码中可以看出
    • 二 Runtime相关的类
      • 1、Core Fuoundation中愿意RunLoop有5个类
        • 2、CFRunLoop
          • Sources0
          • Source1
          • Timers
          • observer
      • 3、RunLoop结构
      • 4、RunLoop运行逻辑
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档