美文网首页底层
iOS-OC底层29:Runloop

iOS-OC底层29:Runloop

作者: MonKey_Money | 来源:发表于2020-11-27 17:34 被阅读0次

    1.前沿

    1.1概念

    Runloop不仅仅是一个运行循环(do-while循环),也是提供了一个入口函数的对象,消息机制处理模式。运行循环从两种不同类型的源接收事件。
    输入源提供异步事件,通常是来自另一个线程或来自不同应用程序的消息。定时器源提供同步事件,发生在预定时间或重复间隔。
    两种类型的源都使用特定于应用程序的处理程序例程来处理事件。除了处理输入源之外,Runloop还会生成有关Runloop行为的通知。
    已注册的运行循环观察器可以接收这些通知并使用它们在线程上执行其他处理。
    runloop官方文档

    1.2runloop实质做了什么事

    runloop实质是一个dowhile循环

    void CFRunLoopRun(void) {    /* DOES CALLOUT */
        int32_t result;
        do {
            // 1.0e10 : 科学技术 1*10^10
            result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
            CHECK_FOR_FORK();
        } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
    }
    

    runloop中的do while循环和我们自己写的有什么区别呢?

      while (1) {
                NSLog(@"hello");
            }
    

    我们自己写的dowhile循环,cpu被大量占用,但是系统中runloop的dowhile并没有大量占有cpu,有此可以推断系统的runloop做了优化。

    1.3.RunLoop的作用

    1.保持程序的持续运行
    dowhile让程序保活。
    2.处理APP中的各种事件(触摸、定时器、performSelector)
    当我们点击屏幕时,GraphicsServices中GSEventRunModal会想runloop发送消息,然后执行CFRunLoopRunSpecific,然后让UIKit处理事件
    3.节省cpu资源、提供程序的性能:该做事就做事,该休息就休息
    当我们的应用没有事情处理时,占有的cpu几乎为0,当我们外部或者系统有事情处理时被唤醒。

    1.4.runloop 的item

    1.block应用:
    CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
    这个item就是让系统调用blcok

    image.png
    2.调timer:
    CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION
    当runloop被这个item唤醒时,调用timer回调。
    image.png
    3.响应source0:
    __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
    当我们UI有事件需要处理时,比如我们实现touchesBegan方法,看堆栈信息
    image.png
    1. 响应source1: CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION
      这个系统的,笔者因为能力有限,所以没有找到出发的方法
    2. GCD主队列:
      CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
      系统启动这个item,然后调用GCD主队列
      image.png
    3. observer源: CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION
      当runloop被这个item唤醒时,就是该调用observer了
      image.png

    2.runloop底层实现

    源码

    2.1. runloop的结构

    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;
        uint32_t _winthread;
        CFMutableSetRef _commonModes;
        CFMutableSetRef _commonModeItems;
        CFRunLoopModeRef _currentMode;
        CFMutableSetRef _modes;
        struct _block_item *_blocks_head;
        struct _block_item *_blocks_tail;
        CFTypeRef _counterpart;
    };
    

    runloop结构体中保存了线程,_commonModes _currentMode,_commonModeItems, _block_item等等信息。

    2.2. runloop和线程关系

    当我们调用CFRunLoopGetCurrent和CFRunLoopGetMain时到底发生了什么?我们从源码分析CFRunLoopGetCurrent和CFRunLoopGetMain最终掉的都是_CFRunLoopGet0

    CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    //如果线程是空的,默认是猪线程
        if (pthread_equal(t, kNilPthreadT)) {
            t = pthread_main_thread_np();
        }
        __CFSpinLock(&loopsLock);
    //判断静态字典是否为空(为空说明时第一次使用)
        if (!__CFRunLoops) {
    //是空的创建,并创建主线程的runloop并保存到__CFRunLoops
            __CFSpinUnlock(&loopsLock);
            CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
            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);
            __CFSpinLock(&loopsLock);
        } 
        CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        __CFSpinUnlock(&loopsLock);
        if (!loop) {
            CFRunLoopRef newLoop = __CFRunLoopCreate(t);
            __CFSpinLock(&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
            __CFSpinUnlock(&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;
    }
    

    1.首先判断传入的线程是否为空,如果是空,设置成主线程
    2.判断__CFRunLoops是否为空,__CFRunLoops是一个Dictionary 以线程为key,runloop为value进行保存,如果为空进行创建,并保存main线程和主runloop,3
    3.试着从__CFRunLoops以线程为key进行取值,如果取到就在下面返回,如果取不到则进行创建。并在下面返回。

    2.3. runloop机制

    image.png
    image.png

    CFRunLoopRun----》CFRunLoopRunSpecific

    SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
        CHECK_FOR_FORK();
        if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
        __CFRunLoopLock(rl);
        
        /// 首先根据modeName找到对应mode
        CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
        
        /// 通知 Observers: RunLoop 即将进入 loop。
        __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
        
        /// 内部函数,进入loop
        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) {
        
        int32_t retVal = 0;
        
        do {  // itmes do
            
            /// 通知 Observers: 即将处理timer事件
            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
            
            /// 通知 Observers: 即将处理Source事件
            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
            
            /// 处理Blocks
            __CFRunLoopDoBlocks(rl, rlm);
            
            /// 处理sources0
            Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
            
            /// 处理sources0返回为YES
            if (sourceHandledThisLoop) {
                /// 处理Blocks
                __CFRunLoopDoBlocks(rl, rlm);
            }
            
            
            /// 判断有无端口消息(Source1)
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                /// 处理消息
                goto handle_msg;
            }
            
            /// 通知 Observers: 即将进入休眠
            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
            __CFRunLoopSetSleeping(rl);
            
            /// 等待被唤醒
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
            
            
            // user callouts now OK again
            __CFRunLoopUnsetSleeping(rl);
            
            /// 通知 Observers: 被唤醒,结束休眠
            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
            
        handle_msg:
            if (被Timer唤醒) {
                /// 处理Timers
                __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
            } else if (被GCD唤醒) {
                /// 处理gcd
                __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            } else if (被Source1唤醒) {
                /// 被Source1唤醒,处理Source1
                __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
            }
            
            /// 处理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;
            }
            
        } while (0 == retVal);
        
        return retVal;
    }
    

    相关文章

      网友评论

        本文标题:iOS-OC底层29:Runloop

        本文链接:https://www.haomeiwen.com/subject/xjfziktx.html