Runtime & Runloop

Runloop:原理篇

2020-03-10  本文已影响0人  码小菜
夜景

目录
一,基本知识
二,Runloop对象
三,底层结构
四,Mode
五,运行流程
六,休眠的原理

一,基本知识

1,含义

运行循环,在程序运行过程中循环的处理事件

2,作用

1>没有runloop

// 执行完return,程序立即退出
int main(int argc, char * argv[]) {
   @autoreleasepool {
       NSLog(@"hello world!");
       return 0;
   }
}

2>有runloop

// 不会执行return,程序不会退出
int main(int argc, char * argv[]) {
   @autoreleasepool {
       int value = 0;
       // 函数内部会启动runloop,runloop会让代码阻塞在此
       value = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
       return value;
   }
}

3>伪代码

int main(int argc, char * argv[]) {
   @autoreleasepool {
       int value = 0;
       do {
           // runloop处理事件或休眠
       } while (1);
       return value;
   }
}
二,Runloop对象

1,CFRunLoopRef源码下载地址

// 获取主线程的runloop对象
CFRunLoopRef main = CFRunLoopGetMain();
// 获取当前线程的runloop对象
CFRunLoopRef current = CFRunLoopGetCurrent();

2,NSRunLoop(对CFRunLoopRef的封装)

// 获取主线程的runloop对象
NSRunLoop *main = [NSRunLoop mainRunLoop];
// 获取当前线程的runloop对象
NSRunLoop *current = [NSRunLoop currentRunLoop];

3,与线程的关系

1>每条线程都有一个runloop对象

2>线程刚创建时并不会创建runloop对象,而是在第一次获取它时创建的

3>主线程的runloop对象会在程序启动时自动创建,子线程的需要手动创建

4>runloop对象会在线程销毁时自动销毁

5>runloop对象存储在一个全局的字典里,线程作为keyrunloop对象作为value

CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    static CFRunLoopRef __main = NULL;
    // 调用_CFRunLoopGet0
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np());
    return __main;
}

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

// 简化代码
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    // 用线程从字典中取出runloop对象
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    // 如果字典中没有该线程对应的runloop对象
    if (!loop) {
        // 创建runloop对象
        CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
        loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        if (!loop) {
            // 以线程作为key把创建的runloop对象存储到字典中
            CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
            loop = newLoop;
        }
        __CFUnlock(&loopsLock);
        CFRelease(newLoop);
    }
    return loop;
}
三,底层结构

1,CFRunLoopRef

typedef struct __CFRunLoop * CFRunLoopRef;

// 简化代码
struct __CFRunLoop {
    pthread_t _pthread;               // 线程对象
    CFMutableSetRef _commonModes;     // 用kCFRunLoopCommonModes标记的mode名称
    CFMutableSetRef _commonModeItems; // 所有添加在kCFRunLoopCommonModes下的事件
    CFRunLoopModeRef _currentMode;    // 当前mode
    CFMutableSetRef _modes;           // 所有mode
};

2,CFRunLoopModeRef

typedef struct __CFRunLoopMode *CFRunLoopModeRef;

// 简化代码
struct __CFRunLoopMode {
    CFStringRef _name;            // 名称
    CFMutableSetRef _sources0;    // CFRunLoopSourceRef集合
    CFMutableSetRef _sources1;    // CFRunLoopSourceRef集合
    CFMutableArrayRef _observers; // CFRunLoopObserverRef数组
    CFMutableArrayRef _timers;    // CFRunLoopTimerRef数组
};

3,关系图

关系图

4,说明

四,Mode

1,作用

将不同类型的source0/source1/observer/timer分隔开,这样runloop在某种mode下运行时,只需要处理一种类型的事件,效率会比较高

2,类型

3,CFRunLoopSourceRef(事件源)

source0

4,CFRunLoopObserverRef(观察者)

// 监听runloop的运行状态
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    switch (activity) {
        case kCFRunLoopEntry:
            NSLog(@"进入runloop");
            break;
        case kCFRunLoopBeforeTimers:
            NSLog(@"即将处理timers");
            break;
        case kCFRunLoopBeforeSources:
            NSLog(@"即将处理sources");
            break;
        case kCFRunLoopBeforeWaiting:
            NSLog(@"即将休眠");
            break;
        case kCFRunLoopAfterWaiting:
            NSLog(@"刚被唤醒");
            break;
        case kCFRunLoopExit:
            NSLog(@"退出runloop");
            break;
        default:
            break;
    }
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
CFRelease(observer);

// 打印
即将处理timers
即将处理sources
即将处理timers
即将处理sources
即将休眠
刚被唤醒
即将处理timers
即将处理sources
即将休眠
刚被唤醒
即将处理timers
即将处理sources
即将休眠

5,CFRunLoopTimerRef(定时器)

timer

6,切换mode

- (void)viewDidLoad {
    [super viewDidLoad];
    
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"进入runloop---%@", CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent()));
                break;
            case kCFRunLoopExit:
                NSLog(@"退出runloop---%@", CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent()));
                break;
            default:
                break;
        }
    });
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
    CFRelease(observer);
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    NSLog(@"%s", __func__);
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
                  willDecelerate:(BOOL)decelerate {
    NSLog(@"%s", __func__);
}

// 打印
-[ViewController scrollViewWillBeginDragging:]
退出runloop---kCFRunLoopDefaultMode 
进入runloop---UITrackingRunLoopMode
-[ViewController scrollViewDidEndDragging:willDecelerate:]
退出runloop---UITrackingRunLoopMode
进入runloop---kCFRunLoopDefaultMode
五,运行流程

1,官方图解

官方

2,自定义图解

自定义

3,源码

入口
// 简化代码
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
    // 通知observers:进入runloop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    // 循环处理事件
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    // 通知observers:退出runloop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
    return result;
}
// 简化代码
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    int32_t retVal = 0;
    
    do {
        // 通知observers:即将处理timers
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        // 通知observers:即将处理sources
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        // 处理blocks
        __CFRunLoopDoBlocks(rl, rlm);
        // 处理sources0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            // 可能会再次处理blocks
            __CFRunLoopDoBlocks(rl, rlm);
        }
        // 是否存在sources1
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            goto handle_msg;
        }
        
        // 通知observers:即将休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        __CFRunLoopSetSleeping(rl);
        // 等待消息唤醒(内部会调用mach_msg函数)
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
        // 通知observers:刚被唤醒
        __CFRunLoopUnsetSleeping(rl);
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        
    handle_msg:
        if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) { // 被timer唤醒
            // 处理timers
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
        } else if (livePort == dispatchPort) { // 被GCD唤醒
            // 处理GCD
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
        } else { // 被source1唤醒
            // 处理sources1
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply);
        }
         
        // 处理blocks
        __CFRunLoopDoBlocks(rl, rlm);
        
        // 是否继续处理事件
        if (sourceHandledThisLoop && stopAfterHandle) { // 否
            retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) { // 否
            retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(rl)) { // 否
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) { // 否
            retVal = kCFRunLoopRunFinished;
        }
    } while (0 == retVal);
    
    return retVal;
}
六,休眠的原理

1,说明

2,图解

休眠
上一篇 下一篇

猜你喜欢

热点阅读