Runloop

Runloop 不仅仅是一个运行循环,而且是一个对像,对像里面提供很多东西。(do-while循环)

提供一个入口,让程序进入do...while循环,保证应用程序不被退出。

runloop的官方文档是在thread里面的一个小分支,runloop和线程是息息相关的。

runloop是一种消息机制的处理模式,例如屏幕点击事件或者timer都会交给timer去处理。

runloop的作用:

1. 保证程序的持续运行。

2. 处理APP中的各种事件(触摸,定时器,performSelector等)。

3. 节省cpu资源,提供程序的性能:改做事就做事,改休息就休息。

void CFRunLoopRun(void) {    /* DOES CALLOUT */

    int32_t result;

    do {

        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);

        CHECK_FOR_FORK();

    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);

}

 

官方文档:

 

APP启动的时候,runloop就开始循环

 在main函数中返回0,APP会直接退出,不会继续运行。

调用timer,查看调用堆栈,__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ 为当前runloop类型

 

搜索__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__源码

调用perforSelector,也会使用__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ [self performSelector:@selector(fire) withObject:nil afterDelay:1.0];, 

调用主队列多线程__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__

 

 调用block,__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__

 

 

Runloop 的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是一一对应的关系:

线程和runloop 是通过key-value的方式进行一一对应

// should only be called by Foundation

// t==0 is a synonym for "main thread" that always works

CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) { //传入一个线程

    if (pthread_equal(t, kNilPthreadT)) {

        t = pthread_main_thread_np();

    }

    __CFLock(&loopsLock);

    if (!__CFRunLoops) {

        __CFUnlock(&loopsLock);

     //生成字典

        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);

        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());

     // 给dict setValue 线程指针 forKey mainLoop 所以线程和runloop是一一对应的关系

        CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);

        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {

            CFRelease(dict);

        }

        CFRelease(mainLoop);

        __CFLock(&loopsLock);

    }

  //这个也是根据线程指针从dict里面去除runloop

    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));

    __CFUnlock(&loopsLock);

    if (!loop) {

        CFRunLoopRef newLoop = __CFRunLoopCreate(t);

        __CFLock(&loopsLock);

        loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));

        if (!loop) {

            CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);

            loop = newLoop;

        }

        // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it

        __CFUnlock(&loopsLock);

        CFRelease(newLoop);

    }

    if (pthread_equal(t, pthread_self())) {

        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);

        if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {

            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);

        }

    }

    return loop;

}

线程会自动销毁,只打印了一次 ,线程和runloop是一一对应的,但是子线程 是默认不开启runloop的,需要在子线程内部加 [[NSRunLoop currentRunLoop] run];

如果需要退出线程在线程内部 [NSThread exit]; 退出runloop,退出之后子线程就没有意义了就会销毁

timer的运行是依赖runloop的,runloop退出的话,timer就失效了

源码分析:

    //CFRunloopMode 研究

    CFRunLoopRef lp = CFRunLoopGetCurrent();

    CFRunLoopMode model = CFRunLoopCopyCurrentMode(lp);

    NSLog(@"model == %@", model);

    CFArrayRef modeArray = CFRunLoopCopyAllModes(lp);

    NSLog(@"modelArray == %@", modeArray);

 

NSRunLoop 是由CFRunLoopRef构建

CFRunLoopRef和线程是一一对应关系

struct __CFRunLoopRunLoop是个结构体,是个对象

    CFRuntimeBase _base;

    pthread_mutex_t _lock;            /* locked for accessing mode list */  lock锁

    __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; 支持windows

    CFMutableSetRef _commonModes;  set类型,说明会有多个Modes

    CFMutableSetRef _commonModeItems; set类型,说明会有多个items

    CFRunLoopModeRef _currentMode;

    CFMutableSetRef _modes;

    struct _block_item *_blocks_head;

    struct _block_item *_blocks_tail;

    CFAbsoluteTime _runTime;

    CFAbsoluteTime _sleepTime;

    CFTypeRef _counterpart;

};

struct __CFRunLoopMode {

    CFRuntimeBase _base;

    pthread_mutex_t _lock;    /* must have the run loop locked before locking this */

    CFStringRef _name;

    Boolean _stopped;

    char _padding[3];

  集合类型,说明每个Mode,都会有多个sources0,sources1,observers,timers

    CFMutableSetRef _sources0;

    CFMutableSetRef _sources1;

    CFMutableArrayRef _observers;

    CFMutableArrayRef _timers;

    CFMutableDictionaryRef _portToV1SourceMap;

    __CFPortSet _portSet;

    CFIndex _observerMask;

#if USE_DISPATCH_SOURCE_FOR_TIMERS

    dispatch_source_t _timerSource;

    dispatch_queue_t _queue;

    Boolean _timerFired; // set to true by the source when a timer has fired

    Boolean _dispatchTimerArmed;

#endif

#if USE_MK_TIMER_TOO

    mach_port_t _timerPort;

    Boolean _mkTimerArmed;

#endif

#if DEPLOYMENT_TARGET_WINDOWS

    DWORD _msgQMask;

    void (*_msgPump)(void);

#endif

    uint64_t _timerSoftDeadline; /* TSR */

    uint64_t _timerHardDeadline; /* TSR */

};

 

一个runloop只能在一个mode下运行,但是可以在不同的mode之间切换。

 

验证当前在defaultMode,并且有4个mode。

下面分析timer是如何加到runloop中的:

 

__CFRunLoopDoBlocks(rl, rlm); //rl是runloop, rlm是runloopMode,意思是在rlm模式先有哪些runLoop需要执行

 

通过链表遍历items,如果有

 

简述下刚才timer的流程:

if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {

            // timer 加入的mode 和 我们现在runloop的mode 相等

            // curr->_mode = kCFRunLoopCommonModes 相等

       // 事务就可以执行 

            doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));

        } else {

            doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));

        }

  

  

 demo:timer 底层cf  fxRunLoopTimerCallBack为一个c语言方法

 

 

//只要runloop状态发生改变,就会回调。fxRunLoopObserveCallBack

 source :

 

 联合体虽然是同一个指针,但是不会覆盖,有位域来控制

source0 只包含一个回调函数指针(标记 signal待处理,wakeup唤醒 runloop处理事件)

任何的触摸事件和响应事件都是source0

source1 包含mach_port 和回调函数指针 (线程之间通信 )

source1 依赖于port,主要使用port于线程间通信(很少用,用起来麻烦),已经内核管理

components里面为传过来的信息

原文地址:https://www.cnblogs.com/coolcold/p/12169459.html