OC原理-RunLoop

1,052 阅读4分钟

RunLoop顾名思义就是运行循环,就是在程序运行过程中循环做一些事情。

应用范围:

1、定时器(Timer)、PerformSelector
2、GCD Async Main Queue
3、事件响应、手势识别、界面刷新
4、网络请求
5、AutoreleasePool

这里里面都用到了runloop。

RunLoop的基本作用:1.保持程序的持续运行 2.处理App中的各种事件(比如触摸事件、定时器事件等)3.节约CPU资源,提高程序性能:该做事时做事,该休息时休息

RunLoop对象

ios中有2套API来访问和使用RunLoop
  • Foundation:NSRunLoop
//获取当前线程的runloop
NSRunLoop *runloop1 = [NSRunLoop currentRunLoop];
//获取主线程的runloop
NSRunLoop *runloop2 = [NSRunLoop mainRunLoop];
  • Core Foundation:CFRunLoopRef
//获取当前线程的runloop
CFRunLoopRef runloop3 = CFRunLoopGetCurrent();
//获取主线程的runloop
CFRunLoopRef runloop4 = CFRunLoopGetMain();
//打印上面4个对象的内存地址
NSLog(@"%p %p %p %p",runloop1,runloop2,runloop3,runloop4);

我们惊讶的发现2套api获取的主线程的runloop竟然不是一个对象。这是因为NSRunLoop对象对CFRunLoopRef对象进行了一层包装,本质上还是CFRunLoopRef对象。

CFRunLoopRef

Core Foundation中关于RunLoop的5个类

CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
typedef struct __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_t _pthread;     //对应的线程
    CFMutableSetRef _commonModes;  //Set<CFStringRef>
    CFMutableSetRef _commonModeItems; //Set<Source/Observer/Timer>
    CFRunLoopModeRef _currentMode;  //当前的CFRunLoopModeRef对象
    CFMutableSetRef _modes;  //存储了一堆的CFRunLoopModeRef对象
    
};
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
    CFStringRef _name; //模式的名字
    CFMutableSetRef _sources0; //存储CFRunLoopSourceRef对象
    CFMutableSetRef _sources1; //存储CFRunLoopSourceRef对象
    CFMutableArrayRef _observers; //存储CFRunLoopObserverRef对象
    CFMutableArrayRef _timers; //存储CFRunLoopTimerRef对象
    
};

他们之间的关系如下图

  • CFRunLoopMode代表RunLoop的运行模式
  • 一个RunLoop包含若干个Mode,每个Mode有包含若干个Source0/Source1/Timer/Observer
  • RunLoop启动时只能选择其中的一个Mode作为currentMode
  • 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入。
  • 如果Mode中没有任何的Source0/Source1/Timer/ObserverRunLoop会立马退出

注意:不同mode下的Source0/Source1/Timer/Observer分隔开来,互不影响,提高效率

CFRunLoopModeRef

Mode有以下几种

kCFRunLoopDefaultMode 默认Mode,主线程通常在这种模式下运行
UITrackingRunLoopMode 追踪Mode,保证UIScrollView滑动顺畅不受其他Mode影响
kCFRunLoopCommonModes 占位Mode,并不是一个真的模式,只是一个标记。相当于 kCFRunLoopDefaultMode+UITrackingRunLoopMode

UIInitializationRunLoopMode App启动后的过渡Mode,启动完成不在使用
GSEventReceiveRunLoopMode   Graphic相关事件Mode,通常用不到

下面两种我们基本不用 可以忽略 每种Mode中有存储这个若干个Source0/Source1/Timer/Observer

  • Source0
    • 触摸事件处理
    • performSelector:onThread:
  • Source1
    • 基于Port的线程间通信
    • 系统事件捕捉
  • Timers
    • NSTimer
    • performSelector:withObject:afterDelay:
  • Observers
    • 用于监听RunLoop的状态
    • UI 刷新(BeforeWaiting)
    • AutoRelease pool (BeforWaiting)

我们看到左侧的调用堆栈从15直接过渡了1,我们在调试台那里输入bt,显示出所有的调用堆栈 Source0、Timers、Observers的其他情况也可以如此证明。

通过上图还发现了一次调用了CFRunLoopRunSpecific__CFRunLoopRun__CFRunLoopDoSources0__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__方法。其中CFRunLoopRunSpecific__CFRunLoopRun为runloop的主题函数,__CFRunLoopDoSources0__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__是Source0的处理函数,同理可推还有Source1/Timer/Observer的处理函数。

CFRunLoopObserverRef

我们可以给runloop添加observer来监听runloop的状态改变。 runloop有以下几种状态

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添加监听

//创建监听对象
//kCFRunLoopAllActivities监听所有对象
//YES重复监听
//0优先级 NSIntegerMax:为优先级最小
//MYRunLoopObserverCallBack回调函数
//Null 这里可以为回调函数传入一些值 在回调函数的info里面获取到
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, MYRunLoopObserverCallBack, NULL);
//添加监听对象
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
//释放监挺对象
CFRelease(observer);
 
 
//监听函数
void MYRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){

}

还有另外一种添加observer方式

CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    //回调block   利用block写到了一起,就用不到info来传递值了
});
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
CFRelease(observer);

RunLoop与线程

源码地址

CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    static CFRunLoopRef __main = NULL; 
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); 
    return __main;
}
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}
//最后动调用了_CFRunLoopGet0并传入了一个*线程*
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
  //如果是空线程那就默认主线程
    if (pthread_equal(t, kNilPthreadT)) {
	t = pthread_main_thread_np();
    }
    __CFLock(&loopsLock);
    if (!__CFRunLoops) {
        __CFUnlock(&loopsLock);
       //创建存储所有runloop对象的字典
	CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
      //生成并存储主线程对象的runloop
	CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
	CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
	if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
	    CFRelease(dict);
	}
	CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    //根据线程获取对象的runloop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    if (!loop) {
      //创建新的runloop
	CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
        //根据线程获取对象的runloop
	loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
	if (!loop) {
    	  //存储runloop
	    CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
	    loop = newLoop;
	}
        __CFUnlock(&loopsLock);
	CFRelease(newLoop);
    }
    return loop;
}

通过上面的源码我们可以看出

  • 每条线程有且只有一个runloop对象
  • app所有的runloop保存在一个全局的Dictionary里,线程为key,runloop为value
  • 线程刚创建时并没有runloop对象,runloop会在第一次获取它的时候创建。主线程的runloop已经自动获取(创建),子线程默认没有runloop
  • runloop会在线程结束时销毁。

RunLoop执行过程

//seconds时间后在rl循环的modeName模式下执行  returnAfterSourceHandled标示处理Source后是不是结束loop
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    //模式不存在或者模式中的source0、source1、timer、observer都不存在  返回kCFRunLoopRunFinished结束loop
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
        Boolean did = false;
        if (currentMode) __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopUnlock(rl);
        return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
    //取出loop的mode保存为previousMode
    CFRunLoopModeRef previousMode = rl->_currentMode;
    //修改loop的mode为传进来的mode
    rl->_currentMode = currentMode;
    int32_t result = kCFRunLoopRunFinished;
    
    //通知observer进入某种mode的loop
	if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    //具体要做的事情
	result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    //通知observer退出某种mode的loop
	if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
	
    //当传进来的mode运行结束后,mode为原来保存的那个
	rl->_currentMode = previousMode;
    return result;
}

/*
rl       运行的runloop
rlm      运行的mode
seconds  超时时间
stopAfterHandle ture:loop处理完事件就退出  false:一直运行到超时或者被手动退出
previousMode runloop中上次的mode
*/
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    int32_t retVal = 0;
    do {
        //通知observer即将处理timer
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        //通知observer即将处理Source
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        //处理block
        __CFRunLoopDoBlocks(rl, rlm);
        //处理Source0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            //处理block
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        msg = (mach_msg_header_t *)msg_buffer;
        //判断有无Source1
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            //有Source1去处理,跳转到handle_msg
            goto handle_msg;
        }
        

        //通知Observer即将休眠
        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        //休眠
        __CFRunLoopSetSleeping(rl);

        CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
        
        // 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。
          // -  一个基于 port 的Source1 的事件。
          // -  一个 Timer 到时间了
          // -  RunLoop 启动时设置的最大超时时间到了
          // -  被手动唤醒
          __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {
              mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg
          }
        //休眠结束
        __CFRunLoopUnsetSleeping(rl);
        //通知observer结束休眠
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        //有Source1去处理,跳转到这里
        handle_msg:;

        if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {//被timer唤醒
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                //执行timer
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
        else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {//被timer唤醒
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                //执行timer
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
        else if (livePort == dispatchPort) { //被GCD唤醒
            //处理GCD Async To Main Queue
	    __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
        } else {//被source1唤醒
            CFRUNLOOP_WAKEUP_FOR_SOURCE();
            voucher_t previousVoucher = _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, (void *)voucherCopy, os_release);
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
            if (rls) {
                mach_msg_header_t *reply = NULL;
                //处理Source1
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
            }
            
        } 

        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);

        //处理Block
        __CFRunLoopDoBlocks(rl, rlm);
            
        //设置返回值retVal
        
        if (sourceHandledThisLoop && stopAfterHandle) {
        ////如果处理事件完毕,启动Runloop时设置参数为一次性执行,设置while参数退出Runloop
            retVal = kCFRunLoopRunHandledSource;
            } else if (timeout_context->termTSR < mach_absolute_time()) {
            //启动runloop时设置的最大运行时间到了,设置while参数退出Runloop
                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)) {
        	//runloop中的source0/source1/timer/observer都没有了,退出runloop
            retVal = kCFRunLoopRunFinished;
        }
    

    } while (0 == retVal);

    return retVal;
}

大体流程如下图: __CFRunLoopRun最终会调用后面的函数来工作。