iOS底层 之 RunLoop攻坚
$[timeformat('2021-10-08T10:56:43+08:00')]

什么是RunLoop

顾名思义,RunLoop就是一个保证程序或者线程持续运行的循环。

runloop 作用

  • 保持程序持续运行
  • 处理各种事件 :触摸事件、定时器时间、selector事件、网络请求等
  • 节省CPU资源,提高程序性能:该干活儿时干活儿,该休息时休息

每一个线程,只对应唯一一个Runloop iOS开发中,UIApplicationMain 函数启动了主线程的 主RunLoop,保证程序的主线程的运行状态,这样UI就不会卡顿。主线程中的runloop是系统主动开启的,开发者手动开启的子线程内的runloop如果不手动开启,子线程执行完就结束了,线程就销毁了。所以,要想手动开启的主线程执行完任务后不被销毁,就需要手动启动RunLoop.

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

RunLoop的五个模式

  • kCFRunLoopDefaultMode:APP默认的模式,通常主线程就是在这个模式下运行
  • UITrackingRunLoopMode: 界面跟踪模式,用于scrollView跟踪触摸滚动,保证滚动时候 不受其他线程影响
  • kCFRunLoopCommonModes : 占位用的mode,不是一个真正的mode
  • NSRunLoopCommonModes: 相当于 NSDefaultRunLoopMode + UITrackingRunLoopMode
  • UIInitializationRunLoopMode: 刚启动APP时,进入的第一个mode启动完后不再使用
  • GSEventReceiveRunLoopMode:接受系统事件的内部mode,通常用不到

模式使用总结:

  1. 一个RunLoop包含多个mode,每个mode包含若干source0、source1、observer、timer
  2. RunLoop 只能选择一个mode作为currentRunLoop的mode。
  3. 如果想要切换mode,需要先退出当前mode,再重新选择一个mode进入
  4. 不同组的Source、timer、observer 互不影响
  5. 如果Mode里面没有任何的source、timer、observer, RunLoop会立马退出,

RunLoop 对象

iOS开发中有两套API来访问和使用RunLoop

  • < Foundation >NSRunLoop
  • < Core Foundation >CFRunLoopRef

NSRunLoop 和 CFRunLoopRef 都代表RunLoop对象。 NSRunLoop 是基于CFRunLoopRef的一层OC封装。

获取RunLoop 的 API 使用:

Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象

Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象

RunLoop 和 线程之间的关系

  • 每个线程都有对应一个唯一的RunLoop对象
  • RunLoop保存在一个全局键值对里,线程作为key,RunLoop作为value
  • 主线程的RunLoop系统创建并启动好了,子线程的RunLoop需要手动创建
  • RunLoop在第一次获取时创建,在线程结束时销毁
  • 线程创建时候,并没有RunLoop对象,所以,执行完代码线程就销毁了。第一次获取线程对象时候创建RunLoop对象,这时候才会循环等待,保证线程不结束,不被销毁。
  • RunLoop会在线程结束时销毁

RunLoop源码

RunLoop 结构体

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;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};

runloop 内部实现源码

// 拿到当前Runloop 调用_CFRunLoopGet0
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}

// 查看_CFRunLoopGet0方法内部
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);
    // 根据传入的主线程获取主线程对应的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);
    }
    
    // 从字典里面拿,将线程作为key从字典里获取一个loop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    
    // 如果loop为空,则创建一个新的loop,所以runloop会在第一次获取的时候创建
    if (!loop) {  
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    
    // 创建好之后,以线程为key runloop为value,一对一存储在键值对中,下次获取的时候,则直接返回字典内的runloop
    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 中的五个类

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef

CFRunLoopRef
CFRunLoopRef

CFRunLoopModeRef
CFRunLoopModeRef

CFRunLoopActivity

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),           // 即将进入RunLoop
    kCFRunLoopBeforeTimers = (1UL << 1),    // 即将处理Timer
    kCFRunLoopBeforeSources = (1UL << 2),   // 即将处理source
    kCFRunLoopBeforeWaiting = (1UL << 5),   // 即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),    // 唤醒后
    kCFRunLoopExit = (1UL << 7),            // 即将推出RunLoop
    kCFRunLoopAllActivities = 0x0FFFFFFFU   
};

监听RunLoop的所有状态

CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry:
            {// 刚进入RunLoop
            }
                break;
            case kCFRunLoopBeforeTimers:
            {// 即将处理timer
            }
                break;
            case kCFRunLoopBeforeSources:
            {// 即将处理source
            }
                break;
            case kCFRunLoopBeforeWaiting:
            {// 即将进入休眠
            }
                break;
            case kCFRunLoopAfterWaiting:
            {// 唤醒后
            }
                break;
            case kCFRunLoopExit:
            {// 即将退出RunLoop
            }
                break;
            default:
                break;
        }
    });

CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);

CFRelease(observer);

RunLoop运行逻辑

RunLoop运行逻辑
RunLoop运行逻辑