美文网首页
IOS runloop 学习笔记

IOS runloop 学习笔记

作者: 蒲公英守候_c082 | 来源:发表于2019-04-24 15:16 被阅读0次

    这次学习 的内容是 runloop

    1.runloop 是什么
    2.runloop 的作用
    3.runloop 和 线程之间的关系
    4.runloop 源码分析 runloop对象和mode
    5.runloop 源码分析 CFRunloopSourceRef & CFRunloopObserveRef
    6.runloop 源码分析

    runloop的源码大概有3900多行

    Runloop的应用

    • 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

    1.runloop 是什么

    • runloop 是一个运行循环 同时它也是一个对象(对象里面会提供很多东西)
    • 处理app中的各种事件 触摸·定时器·performSelector
    • 程序会进入do...while 循环 循环处理一些事情
    /** runloop 源码看 就是个 do while 循环  入口函数  程序进入do...while */
    void CFRunLoopRun(void) {    /* DOES CALLOUT */
        int32_t result;
        do {
            result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
            CHECK_FOR_FORK();
        } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
    }
    

    2.runloop 的作用

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

    3.runloop 和 线程之间的关系

    1. runloop与线程是 一一对应, 一个runloop 对应一个核心的线程,之所以说是核心,是因为runloop是可以嵌套,但是核心只能有一个,他们关系保存在全局的字典里面
    /** 通过下面这段源代码就可以知道 --对应关系 */
    // 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());
            CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
            if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
                CFRelease(dict);
            }
            CFRelease(mainLoop);
            __CFLock(&loopsLock);
        }
        CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    

    2.runloop是专门来管理线程,当线程的runloop开启之后,线程会在执行完成之后进入休眠状态,有任务就会醒来去干活
    3.runloop 在第一次获取时就会被创建,线程结束时会销毁
    4.对主线程来说,runloop在程序启动的时候就会默认创建出来
    5.对子线程来说,runloop是懒加载,只有在用到的时候才会创建,所以在子线程用定时器要注意:确保子线程的runloop被创建,不然定时器不会回调

    4.runloop 源码分析 runloop对象和mode

    mode == kCFRunLoopCommonModes

    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);
        if (modeName == kCFRunLoopCommonModes) {
            CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
            if (NULL == rl->_commonModeItems) {
                rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
            }
            CFSetAddValue(rl->_commonModeItems, rlt);
            if (NULL != set) {
                CFTypeRef context[2] = {rl, rlt};
                /* add new item to all common-modes */
                // timer -- items()
                CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
                CFRelease(set);
            }
        }
    //这里代码只是截取了一部分
    }
    

    这段代码是首先将timer 加入到 commonModes里面去
    timer 最终会加入到 items里面

    timer 和 runloop : timer一定要加入到某一个相应的mode里面去
    通过底层源码知道 timer 就会加入到 items里面
    runloopRun 调用的时候 里面就会有doblock,就是while循环当前的items 一层一层循环查找,直到找到了itme没有 会先进行doit判断,再是block调用

    - (void)cfTimerDemo{
        CFRunLoopTimerContext context = {
            0,
            ((__bridge void *)self),
            NULL,
            NULL,
            NULL
        };
        CFRunLoopRef rlp = CFRunLoopGetCurrent();
        /**
         参数一:用于分配对象的内存
         参数二:在什么是触发 (距离现在)
         参数三:每隔多少时间触发一次
         参数四:未来参数
         参数五:CFRunLoopObserver的优先级 当在Runloop同一运行阶段中有多个CFRunLoopObserver 正常情况下使用0
         参数六:回调,比如触发事件,我就会来到这里
         参数七:上下文记录信息
         */
        CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 0, 1, 0, 0, lgRunLoopTimerCallBack, &context);
        CFRunLoopAddTimer(rlp, timerRef, kCFRunLoopDefaultMode);
    /** 先创建 RunLoop上下文 和 当前CFRunLoop   然后创建CFRunLoopTimer 并调用加入到timer函数 */
    }
    
    void lgRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info){
        NSLog(@"%@---%@",timer,info);
    }
    打印结果   <__NSCFTimer: 0x280b68d80>---<ViewController: 0x111d12700>
    

    5.runloop 源码分析 CFRunloopSourceRef & CFRunloopObserveRef

    先上代码

    - (void)cfObseverDemo{
        
        CFRunLoopObserverContext context = {
            0,
            ((__bridge void *)self),
            NULL,
            NULL,
            NULL
        };
        CFRunLoopRef rlp = CFRunLoopGetCurrent();
        /**
         参数一:用于分配对象的内存
         参数二:你关注的事件
              kCFRunLoopEntry=(1<<0),
              kCFRunLoopBeforeTimers=(1<<1),
              kCFRunLoopBeforeSources=(1<<2),
              kCFRunLoopBeforeWaiting=(1<<5),
              kCFRunLoopAfterWaiting=(1<<6),
              kCFRunLoopExit=(1<<7),
              kCFRunLoopAllActivities=0x0FFFFFFFU
         参数三:CFRunLoopObserver是否循环调用
         参数四:CFRunLoopObserver的优先级 当在Runloop同一运行阶段中有多个CFRunLoopObserver 正常情况下使用0
         参数五:回调,比如触发事件,我就会来到这里
         参数六:上下文记录信息
         */
        CFRunLoopObserverRef observerRef = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, lgRunLoopObserverCallBack, &context);
        CFRunLoopAddObserver(rlp, observerRef, kCFRunLoopDefaultMode);
    }
    
    void lgRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
        NSLog(@"%lu-%@",activity,info);
    }
    

    2019-04-24 11:37:13.352235+0800 2-<ViewController: 0x105514210>
    2019-04-24 11:37:13.352460+0800 4-<ViewController: 0x105514210>
    2019-04-24 11:37:13.352661+0800 32-<ViewController: 0x105514210>
    2019-04-24 11:37:13.353129+0800 64-<ViewController: 0x105514210>
    2019-04-24 11:37:13.353419+0800 2-<ViewController: 0x105514210>
    2019-04-24 11:37:13.353608+0800 4-<ViewController: 0x105514210>
    2019-04-24 11:37:13.353743+0800 32-<ViewController: 0x105514210>
    2019-04-24 11:37:14.306024+0800 64-<ViewController: 0x105514210>
    2019-04-24 11:37:14.306992+0800 2-<ViewController: 0x105514210>
    2019-04-24 11:37:14.307225+0800 4-<ViewController: 0x105514210>
    2019-04-24 11:37:14.307430+0800 32-<ViewController: 0x105514210>
    2019-04-24 11:38:00.005751+0800 64-<ViewController: 0x105514210>
    2019-04-24 11:38:00.008181+0800 2-<ViewController: 0x105514210>
    2019-04-24 11:38:00.008396+0800 4-<ViewController: 0x105514210>
    2019-04-24 11:38:00.008604+0800 32-<ViewController: 0x105514210>

    打印结果的时候回发现, 在后面相隔几十秒会跳动一下,这个时间时间段没事干,他会处于休眠模式,会让一些状态改变
    observe总结: 说简单点observer就是监听runloop的状态

    source 有 source0 和 source1

    struct __CFRunLoopSource {
        CFRuntimeBase _base;
        uint32_t _bits;
        pthread_mutex_t _lock;
        CFIndex _order;            /* immutable */
        CFMutableBagRef _runLoops;
        union {   //联合体 共享同一个地址 ,可以大大节省内存
            CFRunLoopSourceContext version0;    /* immutable, except invalidation */
            CFRunLoopSourceContext1 version1;    /* immutable, except invalidation */
        } _context;
    };
    

    source0 只包含一个 回调函数指针 会处理APP内部事件,APP自己负责管理 (触发) UIEvent
    source1 包含一个mach_port 以及 回调函数指针 被用于通过内核和其他线程相互发送消息

    小知识点

    子线程runloop 默认不开启
    timer 依赖于runloop

    相关文章

      网友评论

          本文标题:IOS runloop 学习笔记

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