美文网首页
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