RunLoop

#概念
RunLoop事件接收分发机制的一个实现,是线程相关的基础框架的一部分,一个RunLoop就是一个事件处理的循环,用来不停的调度工作以及处理输入事件。

RunLoop本质是一个do-while循环,没事做就休息,来活了就干活。与普通的while循环是有区别的,普通的while循环会导致CPU进入忙等待状态,即一直消耗cpu,而RunLoop则不会,RunLoop是一种闲等待,即RunLoop具备休眠功能

#####RunLoop的作用

  • 保持程序的持续运行
  • 处理APP中的各种事件(触摸、定时器、performSelector)
  • 节省cpu资源、提供程序的性能:该做事就做事,该休息就休息

RunLoop源码的下载地址

#RunLoop和线程的关系
首先,iOS 开发中能遇到两个线程对象:pthread_tNSThread,两者是一一对应的,可以通过pthread_main_thread_np()[NSThread mainThread]来获取主线程;也可以通过 pthread_self()[NSThread currentThread]来获取当前线程。CFRunLoop是基于pthread来管理的。
苹果不允许直接创建 RunLoop,它只提供了两个自动获取的函数:CFRunLoopGetMain()CFRunLoopGetCurrent()

// 主运行循环
 CFRunLoopRef mainRunloop = CFRunLoopGetMain();
 // 当前运行循环
 CFRunLoopRef currentRunloop = CFRunLoopGetCurrent();

/********** CFRunLoopGetMain ********/
CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    static CFRunLoopRef __main = NULL; // no retain needed
    //pthread_main_thread_np 主线程
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main;
}

// 保存RunLoop的全局字典,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef __CFRunLoops = NULL;
/*********_CFRunLoopGet0 *******/

// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    //如果t不存在,则标记为主线程(即默认情况,默认是主线程)
    if (pthread_equal(t, kNilPthreadT)) {
        t = pthread_main_thread_np();
    }
    __CFSpinLock(&loopsLock);
    if (!__CFRunLoops) {
        __CFSpinUnlock(&loopsLock);
        // 第一次进入时,初始化全局Dic,并先为主线程创建一个 RunLoop。
        //创建全局字典,标记为kCFAllocatorSystemDefault
        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        //通过主线程 创建主运行循环
        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        //利用dict,进行key-value绑定操作,即可以说明,线程和runloop是一一对应的
        // dict : key value
        CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        
        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
            CFRelease(dict);
        }
        
        CFRelease(mainLoop);
        __CFSpinLock(&loopsLock);
    }
    //通过其他线程获取runloop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFSpinUnlock(&loopsLock);
    if (!loop) {
        //如果没有获取到,则新建一个运行循环(所以子线程不获取就不创建RunLoop)
        CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFSpinLock(&loopsLock);
        loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        if (!loop) {
            //将新建的runloop 与 线程进行key-value绑定
            CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
            loop = newLoop;
        }
        // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
        __CFSpinUnlock(&loopsLock);
        CFRelease(newLoop);
    }
    if (pthread_equal(t, pthread_self())) {
        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
        if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
// 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop。
            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
        }
    }
    return loop;
}

从上面的代码可以看出

  • Runloop只有两种,一种是主线程的, 一个是其他线程
  • 线程RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary里。
  • 主线程RunLoop已经自动获取(创建)子线程默认没有开启RunLoop

RunLoop的创建

根据CFRunLoopRef的定义得知RunLoop为一个对象,通过__CFRunLoop结构体进行创建

typedef struct __CFRunLoop * CFRunLoopRef;

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; // runloop和线程是一一对应关系,每个runloop内部会保留一个对应的线程
    uint32_t _winthread;
    CFMutableSetRef _commonModes;//标记为common的mode的集合
    CFMutableSetRef _commonModeItems;//commonMode的item集合
    CFRunLoopModeRef _currentMode;// 当前的模式
    CFMutableSetRef _modes;// CFRunLoopModeRef类型的集合,相对NSArray有序,Set为无序集合
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};

/**********  __CFRunLoopMode *************/
struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;	/* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;

};

截屏2022-03-03 上午10.24.55.png
根据__CFRunLoop__CFRunLoopMode结构体属性可以看出,每个RunLoop中包含了多个ModeMode里面又包含了多个Source/Observer/Timer,其中source分为source0source1多个mode里,有且仅有一个currentMode。如果要切换Mode,只能退出当前 Loop,再重新指定一个Mode进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响
截屏2022-03-03 上午10.24.09.png

###ModeItem

Source & Timer & Observer统称为item,一个item可以被同时加入多个mode。但一个 item被重复加入同一个 mode时是不会有效果的。如果一个mode一个item 都没有,则RunLoop会直接退出,不进入循环

  • Source表示可以唤醒RunLoop的一些事件,基于CFRunLoopSourceRef,是事件产生的地方,分为source0source1

    • source0:表示非系统事件,即用户自定义的事件,只包含了一个回调 ,它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个Source标记为待处理,然后手动调用CFRunLoopWakeUp(runloop)来唤醒RunLoop,让其处理这个事件。
    • source1:表示系统事件,主要负责底层的通讯具备唤醒能力,包含了一个 mach_port一个回调 ,被用于通过内核和其他线程相互发送消息。这种Source主动唤醒RunLoop 的线程。
  • Timer:就是常用NSTimer定时器这一类,基于CFRunLoopTimerRef,是基于时间的触发器,它和NSTimertoll-free bridged的,可以混用。其包含一个时间长度一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。

struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits; //标记fire状态
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop; //添加该timer的runloop
    CFMutableSetRef _rlModes; //存放所有 包含该timer的 mode的 modeName,意味着一个timer可能会在多个mode中存在
    CFAbsoluteTime _nextFireDate; // 下一次触发时间
    CFTimeInterval _interval;    //执行的时间间隔
    CFTimeInterval _tolerance;   //时间偏差
    uint64_t _fireTSR;      // 触发时间
    CFIndex _order;         /* immutable */
    CFRunLoopTimerCallBack _callout;    // 回调
    CFRunLoopTimerContext _context;  // 回调内容
};
  • Observer: 主要用于监听RunLoop的状态变化,并作出一定响应,基于CFRunLoopObserverRef,是观察者,结构体内部包含着一个_runloop成员, 表明每个Observer同时只能监听一个RunLoop, 每个 Observer 都包含了一个回调,当 RunLoop状态发生变化时,观察者就能通过回调接受到这个变化,主要有这几个状态:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    //进入RunLoop
    kCFRunLoopEntry = (1UL << 0),
    //即将处理Timers
    kCFRunLoopBeforeTimers = (1UL << 1),
    //即将处理Source
    kCFRunLoopBeforeSources = (1UL << 2),
    //即将进入休眠
    kCFRunLoopBeforeWaiting = (1UL << 5),
    //被唤醒
    kCFRunLoopAfterWaiting = (1UL << 6),
    //退出RunLoop
    kCFRunLoopExit = (1UL << 7),
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};
  • Item类型
    • block应用:__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__

    • 调用timer:__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__

    • 响应source0: __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__

    • 响应source1: __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__

    • GCD主队列:__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__

    • observer源: __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__

可以根据苹果官方文档针对RunLoop处理不同源的图示
截屏2022-03-03 上午10.56.49.png

  • 管理item接口
CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);

Timer为例

void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return;
    if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
    __CFRunLoopLock(rl);
    
    // 重点 : kCFRunLoopCommonModes
    if (modeName == kCFRunLoopCommonModes) {
        //如果是kCFRunLoopCommonModes 类型
       
        CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
        
        if (NULL == rl->_commonModeItems) {
            rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        }
        //runloop与mode 是一对多的, mode与item也是一对多的
        CFSetAddValue(rl->_commonModeItems, rlt);
        if (NULL != set) {
            CFTypeRef context[2] = {rl, rlt};
            /* add new item to all common-modes */
            //执行
            CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
            CFRelease(set);
        }
    } else {
        //如果是非commonMode类型
        //查找runloop的模型
        CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
        if (NULL != rlm) {
            if (NULL == rlm->_timers) {
                CFArrayCallBacks cb = kCFTypeArrayCallBacks;
                cb.equal = NULL;
                rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
            }
        }
        //判断mode是否匹配
        if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
            __CFRunLoopTimerLock(rlt);
            if (NULL == rlt->_runLoop) {
                rlt->_runLoop = rl;
            } else if (rl != rlt->_runLoop) {
                __CFRunLoopTimerUnlock(rlt);
                __CFRunLoopModeUnlock(rlm);
                __CFRunLoopUnlock(rl);
                return;
            }
            // 如果匹配,则将runloop加进去,而runloop的执行依赖于  [runloop run]
            CFSetAddValue(rlt->_rlModes, rlm->_name);
            __CFRunLoopTimerUnlock(rlt);
            __CFRunLoopTimerFireTSRLock();
            __CFRepositionTimerInMode(rlm, rlt, false);
            __CFRunLoopTimerFireTSRUnlock();
            if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) {
                // Normally we don't do this on behalf of clients, but for
                // backwards compatibility due to the change in timer handling...
                if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
            }
        }
        if (NULL != rlm) {
            __CFRunLoopModeUnlock(rlm);
        }
    }
   
    __CFRunLoopUnlock(rl);
}

RunLoop 执行流程

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */

    // 通知Observers, 进入RunLoop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    // RunLoop里面具体要做的事情, 主要是一个循环
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    // 通知Observers, 退出 RunLoop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    return result;
}

  
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    
    
    mach_port_name_t dispatchPort = MACH_PORT_NULL;
 
    // 当前是否为主线程
    bool cond1 = pthread_main_np();
    // _CFGetTSD(__CFTSDKeyIsInGCDMainQ) 从预先分配的插槽中获取主线程的一些特定数据
    // CF源码中没有搜索到_CFSetTSD(__CFTSDKeyIsInGCDMainQ)的调用,推测但不完全确定_CFGetTSD(__CFTSDKeyIsInGCDMainQ) 为空
    bool cond2 = 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ);
    // 当前是否为主线程
    bool cond3 = CFRunLoopGetMain() == rl;
    // 当前的mode包含在commonModes里面
    bool cond4 = CFSetContainsValue(rl->_commonModes, rlm->_name);
 
    // 当前为主线程且mode被标记为common,dispatchPort才会被赋值
    if (cond1 && cond2 && cond3 && cond4) {
        dispatchPort = _dispatch_get_main_queue_port_4CF();
    }

 
    Boolean didDispatchPortLastTime = true;

    int32_t retVal = 0;
    do {

        __CFRunLoopUnsetIgnoreWakeUps(rl);

        // 通知observers, 即将处理Timers
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        // 通知observers, 即将处理Sources
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        // 处理Blocks
        __CFRunLoopDoBlocks(rl, rlm);

        // 处理source0
        if (__CFRunLoopDoSources0(rl, rlm, stopAfterHandle)) {
            // 处理Blocks
            __CFRunLoopDoBlocks(rl, rlm);
        }

        // timeout 传入0 表示立即返回 传入TIMEOUT_INFINITY 表示等待到消息再返回
        
        // 当前为主线程且mode被标记为common,且非第一次循环
        if (dispatchPort && !didDispatchPortLastTime) {
            // 主线程里有消息处理,直接跳转到handle_msg
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                goto handle_msg;
            }
        }
        didDispatchPortLastTime = false;
 
        // 通知observers, 即将进入休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        __CFRunLoopSetSleeping(rl);

        // 线程进入休眠,等待唤醒
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

        __CFRunLoopUnsetSleeping(rl);
        // 通知observers, 已经结束休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

        // 处理消息
        handle_msg:;

        if (msg_is_timer) {
            // 处理timer
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time())
        } else if (msg_is_dispatch) {
            // 处理GCD  如果有dispatch到main_queue的block,执行block
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
        } else {
            // 处理Source1
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
 
        }
 
        // 处理Blocks
        __CFRunLoopDoBlocks(rl, rlm);
        

          // 设置返回值
       if (sourceHandledThisLoop && stopAfterHandle) {
          //stopAfterHandle 是传入的标记,如果处理完事件就返回
          retVal = kCFRunLoopRunHandledSource;
       } else if (timeout_context->termTSR < mach_absolute_time()) {
           //设置了超时时间,超时返回
          retVal = kCFRunLoopRunTimedOut;
       } else if (__CFRunLoopIsStopped(rl)) {
            // 被外部调用者强制停止了
          __CFRunLoopUnsetStopped(rl);
         retVal = kCFRunLoopRunStopped;
       } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
          // 内部source,observers,timers 都空了,返回
          retVal = kCFRunLoopRunFinished;
       }
        
    // 如果不进入上述的条件,继续循环
    } while (0 == retVal);

    if (timeout_timer) {
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }

    return retVal;
}

截屏2022-03-03 上午10.54.43.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值