Runloop【自我总结版】

2018-11-15  本文已影响3人  小白猿

简介

  1. 起因
  1. 基本设计
    RunLoop根据以上的关键点进行设计
  1. OSX/iOS系统中的RunLoop对象
  1. 苹果不允许直接创建 RunLoop,它只提供了两个自动获取的函数:CFRunLoopGetMain()CFRunLoopGetCurrent()

详细介绍

1.与线程的关系

先看一段代码

/// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// 访问 loopsDic 时的锁
static CFSpinLock_t loopsLock;
/**
 获取(创建)RunLoop的方法
 @param t 线程
 */
// 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);
    // 判断主线程对应的RunLoop是否存在,不存在的话,执行里面的代码
    if (!__CFRunLoops) {
        __CFUnlock(&loopsLock);
        // 主线程对应的RunLoop是默认创建好的
        // 创建字典(以线程为key,以RunLoop为value)
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        // 创建(获取)主线程对应的RunLoop
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        // 字典存储: 以线程为key,以RunLoop为value
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    // 从字典中获取子线程对应的RunLoop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    
    if (!loop) {
        // 如果子线程对应的RunLoop不存在,就创建子线程对应的RunLoop
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        // 把当前子线程对应的RunLoop存储到字段中,以当前子线程为key
    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 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)。

总结上面的代码关系:

  1. 每条线程都有唯一的一个与之对应的RunLoop对象,其关系是保存在一个全局的 Dictionary 里
  2. 主线程的RunLoop已经自动创建好,子线程的RunLoop需要主动创建,并且手动开启
  3. RunLoop在第一次获取时创建,在线程结束时销毁

tip 代码演示一下获取RunLoop的代码

2. RunLoop的结构

  1. 整体结构介绍
    RunLoop 结构图如下,其内部由不同mode(运行模型)构成,每个mode又包含一个或者多个的Source/Timer/Observer,这是大致结构,其中有几个要点:
runloop结构图
  1. 在OSX/iOS系统提供的类
    在 CoreFoundation 里面关于 RunLoop 有5个类:
* CFRunLoopRef           // 获取(创建)RunLoop
* CFRunLoopModeRef       // 运行模式
* CFRunLoopSourceRef     // 事件源
* CFRunLoopTimerRef      // 定时器
* CFRunLoopObserverRef   // 观察者

3.运行模式 mode介绍

转入其他日志https://www.jianshu.com/p/59f525c3c729

  1. 运行模式分类
上一篇 下一篇

猜你喜欢

热点阅读