iOS中的RunLoop

2018-04-05  本文已影响102人  ziyouzhe4

runLoop表面意思是运行循环,程序在运行过程中循环做一些事情

应用范畴:

<如果没有runLoop以上基本会失效.每一个线程都有唯一的一个与之对应的runLoop>

runLoop基本作用
runLoop对象 : iOS 中有2套API来访问和使用runLoop
runLoop和线程关系
获取runLoop对象
// Foundation框架下
[NSRunLoop currentRunloop]; // 获取当前线程的runLoop对象
[NSRunLoop mainRunLoop]; // 获取主线程的runLoop对象
// Core Foundation 下
CFRunloopGetCurrent();  // 获取当前线程的runLoop对象
CGRunLoopGetMain(); // 获取主线程的runLoop对象
获取runLoop源码.png

RunLoop相关的类

// 源码

typedef struct __CFRunLoop * CGRunLoopRef;

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;  // 集合模式, 下面的  __CFRunLoopMode
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};

struct __CFRunLoopMode {
    CFMutableSetRef _sources0; //
    CFMutableSetRef _sources1; //
    CFMutableArrayRef _observers;  //
    CFMutableArrayRef _timers; //
};

<👆源码得出结论 : 一个runLoop含有很多modes,modes都是source0(CFRunLoopSourceRef对象),source1,timers(CFRunLoopTimerRef对象),observers(CFRunLoopObserverRef对象) 👇的图可以解释 : >

runLoop内部结构.png

常见的运行模式

RunLoop运行逻辑

Source0
Source1

基于Port的线程间通信

Timers
Observer

运行步骤:

👇是一个touch事件, 控制台输入 bt, 查看调用栈 :

image.png
👇源码分析
//
//  RunLoop.c
//
//  Created by majianjie on 2018/3/28.
//  Copyright © 2018年 JJCoder. All rights reserved.
//

#include "RunLoop.h"

// 开始调用 CFRunLoopRun
void CFRunLoopRun(void) {    /* DOES CALLOUT */
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}


SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */

    // 通知 Observer 进入runLoop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);

    // runLoop 核心逻辑
     __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);

    // 通知observer 退出runLoop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    return result;

}


/* rl, rlm are locked on entrance and exit */
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {

    int32_t retVal = 0;

    do {
        // 1. 通知observers 将要处理 timers
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        // 2. 通知observers 将要处理 sources0
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        // 3. 通知observers 将要处理 blocks
        __CFRunLoopDoBlocks(rl, rlm);
        // 4. 通知observers 将要处理 sources0
        __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            __CFRunLoopDoBlocks(rl, rlm);
        }

        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);

        // 5. MachPort 监听 如果有source1事件 就跳到 handle_msg
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            goto handle_msg;
        }

        // 6. 通知observer即将进入休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        // 7. 进入休眠 等待其他消息唤醒runLoop
        __CFRunLoopSetSleeping(rl);
        __CFPortSetInsert(dispatchPort, waitSet);

        do {
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

        } while (1);


        // 8. 醒了
        __CFPortSetRemove(dispatchPort, waitSet);
        __CFRunLoopSetIgnoreWakeUps(rl);
        __CFRunLoopUnsetSleeping(rl);// 不睡觉了

        // 9. 通知observer 我醒了
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

        // 10.  处理 handle_msg
    handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);


        // 11 看看是谁唤醒了runLoop 进行相应的处理

        if (MACH_PORT_NULL == livePort) {
            CFRUNLOOP_WAKEUP_FOR_NOTHING();
            // handle nothing
        } else if (livePort == rl->_wakeUpPort) {
            CFRUNLOOP_WAKEUP_FOR_WAKEUP();
            // do nothing on Mac OS

        }else if (/*被timer唤醒的*/) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
          // 处理timer
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time()
        }
        else if (livePort == dispatchPort/*被gcd唤醒的*/) {
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);

        } else {
            // 被source1唤醒的
            CFRUNLOOP_WAKEUP_FOR_SOURCE();
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)

        }

        //  执行block
        __CFRunLoopDoBlocks(rl, rlm);

        // 根据前面执行的结果 来决定 做什么? / 怎么做?
        if (sourceHandledThisLoop && stopAfterHandle) {
            retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {
            retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(rl)) {
            __CFRunLoopUnsetStopped(rl);
            retVal = kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            rlm->_stopped = false;
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            retVal = kCFRunLoopRunFinished;
        }

    } while (0 == retVal);


        // 如果上面一轮下面 retVal 仍然等于 0  继续循环 否则 退出当前runLoop循环


    // 释放相关资源
    if (timeout_timer) {
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }

    // 返回了
    return retVal;
}


验证 GCD的一些操作 也是通过 RunLoop来处理的 ?

image.png

👆调用栈 中有 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE 方法调用,而 源码分析中 有这个函数的处理,是否可以说明 GCD 也是依赖runLoop来处理的呢?

补:

image.png
上一篇 下一篇

猜你喜欢

热点阅读