• 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里面为传过来的信息

  • 相关阅读:
    Leetcode刷题记录:构建最大数二叉树
    Leetcode刷题记录:编码并解码短网址
    Python获取数字的二进制值
    SQL文件的BOM问题导致的invalid character错误及解决
    修改服务的运行权限,解决SVN Post Commit问题
    将iPod中的音乐拷贝到Mac中
    Linux下创建可执行bin安装文件
    Mac下使用XLD转换无损音乐Ape
    Mac环境下配置Tomcat+Eclipse
    Curl操作Elasticsearch的常用方法
  • 原文地址:https://www.cnblogs.com/coolcold/p/12169459.html
Copyright © 2020-2023  润新知