RunLoop浅析

##RunLoop浅析

参考文章:`https://www.jianshu.com/p/c38b5741919b`

源码:`https://opensource.apple.com/tarballs/CF/`

    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

讲讲 RunLoop,项目中有用到吗?

runloop内部实现逻辑?

runloop和线程的关系?

timer 与 runloop 的关系?

程序中添加每3秒响应一次的NSTimer,当拖动tableview时timer可能无法响应要怎么解决?

runloop 是怎么响应用户操作的, 具体流程是什么样的?

说说runLoop的几种状态

runloop的mode作用是什么?

###一 什么是runloop

1.顾名思义

    运行循环

2.应用范畴

    定时器(Timer)、PerformSelector

    GCD Async Main Queue

    事件响应、手势识别、界面刷新

    网络请求

    AutoreleasePool

程序并不会马上退出,而是保持运行状态

###二 RunLoop的基本作用

    1.保持程序的持续运行

    2.处理App中的各种事件(比如触摸事件、定时器事件等)

    3.节省CPU资源,提高程序性能:该做事时做事,该休息时休息

    ......

RunLoop在跑圈过程中,当接收到Input sources 或者 Timer sources时就会交给对应的处理方去处理。当没有事件消息传入的时候,RunLoop就休息了。

###三. RunLoop在哪里开启

我们想象一个场景:为什么App程序启动之后能够持续运行在前台呢?

    int main(int argc, char * argv[]) {

        @autoreleasepool {

            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

        }

    }

    从main函数中可以看出来 是因为UIApplicationMain方法的执行,那么UIApplicationMain内部做了些什么事能保持程序不退出呢?这就是RunLoop的功劳了。

进入UIApplicationMain

    UIKIT_EXTERN int UIApplicationMain(int argc, char *argv[], NSString * __nullable principalClassName, NSString * __nullable delegateClassName);

我们知道主线程一开起来,就会跑一个和主线程对应的RunLoop,那么RunLoop一定是在程序的入口main函数中开启

在UIApplicationMain函数中,开启了一个和主线程相关的RunLoop,导致UIApplicationMain不会返回,一直在运行中,也就保证了程序的持续运行。

    UIApplicationMain的大致实现原理就是:(伪代码)

        int retVal = 0;

        do {

        int message = sleep_and_wait(); //睡眠中等待消息(比如详情点击各种事件)

        retVal = proess_message(message);//处理消息,更改返回值,如果为0,代表程序退出,不为0,程序持续运行。

        }while(retVal == 0);

###四. RunLoop的源码

    // 用DefaultMode启动

    void CFRunLoopRun(void) {  /* DOES CALLOUT */

        int32_t result;

        do {

        //kCFRunLoopDefaultMode,默认情况下,runLoop是在这个mode下运行的

            result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);

            CHECK_FOR_FORK();

        } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);

    }


        SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {    /* DOES CALLOUT */

    CHECK_FOR_FORK();

    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);

    }

该方法,可以设置runLoop运行在哪个mode下modeName,超时时间seconds,以及是否处理完事件就返回returnAfterSourceHandled。

这两个方法实际调用的是同一个方法CFRunLoopRunSpecific,其返回是一个SInt32类型的值,根据返回值,来决定runLoop的运行状况。

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

    CHECK_FOR_FORK();

    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;

    __CFRunLoopLock(rl);


    /// 首先根据modeName找到对应mode

    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);

    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {

Boolean did = false;

if (currentMode) __CFRunLoopModeUnlock(currentMode);

__CFRunLoopUnlock(rl);

return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;

    }

      volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);

    CFRunLoopModeRef previousMode = rl->_currentMode;

    rl->_currentMode = currentMode;

    int32_t result = kCFRunLoopRunFinished;

      /// 通知 Observers: RunLoop 即将进入 loop

if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);

/// 内部函数,进入loop,具体要做的事情

result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);

/// 通知 Observers: RunLoop 即将退出

if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

        __CFRunLoopModeUnlock(currentMode);

        __CFRunLoopPopPerRunData(rl, previousPerRun);

rl->_currentMode = previousMode;

    __CFRunLoopUnlock(rl);

      return result;

    }

核心函数

    static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {


    int32_t retVal = 0;

    do {


        /// 通知 Observers:RunLoop即将触发 Timer 回调。

        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);


        ///  通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。

        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)


        /// 执行被加入的block

        __CFRunLoopDoBlocks(rl, rlm);


        ///  RunLoop 触发 Source0 (非port) 回调

        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);


        /// 处理sources0返回为YES

        if (sourceHandledThisLoop) {

            /// 执行被加入的block

            __CFRunLoopDoBlocks(rl, rlm);

        }



        /// 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息

        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {

            /// 处理消息

            goto handle_msg;

        }


        /// 通知 Observers: RunLoop 的线程即将进入休眠(sleep)

        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);

        __CFRunLoopSetSleeping(rl);


        /// 等待被唤醒

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


        // user callouts now OK again

        __CFRunLoopUnsetSleeping(rl);


        /// 通知 Observers: 被唤醒,结束休眠

        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);


    handle_msg:

        if (被Timer唤醒) {

            /// 处理Timers

            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());

        } else if (被GCD唤醒) {

            /// 处理gcd

            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);

        } else if (被Source1唤醒) {

            /// 被Source1唤醒,处理Source1

            __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);


    return retVal;

    }

    内部调用才是真正处理事件的函数,通过上面bt打印全部堆栈信息也可以得到验证。

    __CFRunLoopDoObservers 内部调用 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__

    __CFRunLoopDoBlocks 内部调用 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__

    __CFRunLoopDoSources0 内部调用 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__

    __CFRunLoopDoTimers 内部调用 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__

    GCD 调用 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__

    __CFRunLoopDoSource1 内部调用 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__

###五. RunLoop对象

    Fundation框架 (基于CFRunLoopRef的封装)

    NSRunLoop对象


    CoreFoundation

    CFRunLoopRef对象


如何获得RunLoop对象

    Foundation

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

    [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象


    Core Foundation

    CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象

    CFRunLoopGetMain(); // 获得主线程的RunLoop对象

###六. RunLoop和线程间的关系

    每条线程都有唯一的一个与之对应的RunLoop对象

    RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value

    主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建

    RunLoop在第一次获取时创建,在线程结束时销毁

通过源码查看上述对应

    // 拿到当前Runloop 调用_CFRunLoopGet0

    CFRunLoopRef CFRunLoopGetCurrent(void) {

        CHECK_FOR_FORK();

        CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);

        if (rl) return rl;

        return _CFRunLoopGet0(pthread_self());

    }

    CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {

        if (pthread_equal(t, kNilPthreadT)) {

            t = pthread_main_thread_np();

        }

        __CFSpinLock(&loopsLock);

        if (!__CFRunLoops) {

            __CFSpinUnlock(&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);

            __CFSpinLock(&loopsLock);

        }

        // 从字典里面拿,将线程作为key从字典里获取一个loop

        CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));

        __CFSpinUnlock(&loopsLock);

        // 如果loop为空,则创建一个新的loop,所以runloop会在第一次获取的时候创建

        if (!loop) {

            CFRunLoopRef newLoop = __CFRunLoopCreate(t);

            __CFSpinLock(&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

            __CFSpinUnlock(&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对象即可[NSRunLoop currentRunLoop];如果不获取,那子线程就不会创建与之相关联的RunLoop,并且只能在一个线程的内部获取其 RunLoop

    [NSRunLoop currentRunLoop];方法调用时,会先看一下字典里有没有存子线程相对用的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;

        };

除一些记录性属性外,主要来看一下以下两个成员变量

        CFRunLoopModeRef _currentMode;

        CFMutableSetRef _modes;

CFRunLoopModeRef其实是指向__CFRunLoopMode结构体的指针,__CFRunLoopMode结构体源码如下

    typedef struct __CFRunLoopMode *CFRunLoopModeRef; //RunLoop的运行模式

    struct __CFRunLoopMode {

        CFRuntimeBase _base;

        pthread_mutex_t _lock;  /* must have the run loop locked before locking this */

        CFStringRef _name;

        Boolean _stopped;

        char _padding[3];

        CFMutableSetRef _sources0;

        CFMutableSetRef _sources1;

        CFMutableArrayRef _observers;

        CFMutableArrayRef _timers;

        CFMutableDictionaryRef _portToV1SourceMap;

        __CFPortSet _portSet;

        CFIndex _observerMask;

        #if USE_DISPATCH_SOURCE_FOR_TIMERS

        dispatch_source_t _timerSource;

        dispatch_queue_t _queue;

        Boolean _timerFired; // set to true by the source when a timer has fired

        Boolean _dispatchTimerArmed;

        #endif

        #if USE_MK_TIMER_TOO

        mach_port_t _timerPort;

        Boolean _mkTimerArmed;

        #endif

        #if DEPLOYMENT_TARGET_WINDOWS

        DWORD _msgQMask;

        void (*_msgPump)(void);

        #endif

        uint64_t _timerSoftDeadline; /* TSR */

        uint64_t _timerHardDeadline; /* TSR */

    };

主要查看以下成员变量

        CFMutableSetRef _sources0;

        CFMutableSetRef _sources1;

        CFMutableArrayRef _observers;

        CFMutableArrayRef _timers;

CFRunLoopModeRef代表RunLoop的运行模式,一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer,而RunLoop启动时只能选择其中一个Mode作为currentMode

Source1/Source0/Timers/Observer分别代表什么

  1. Source1 : 基于Port的线程间通信

  2. Source0 : 触摸事件,PerformSelectors

###八. 详解RunLoop相关类及作用

  CFRunLoopRef - 获得当前RunLoop和主RunLoop

  CFRunLoopModeRef - RunLoop 运行模式,只能选择一种,在不同模式中做不同的操作

  CFRunLoopSourceRef - 事件源,输入源

  CFRunLoopTimerRef - 定时器时间

  CFRunLoopObserverRef - 观察者

1. CFRunLoopModeRef

  CFRunLoopModeRef代表RunLoop的运行模式

  一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source、Timer、Observer

  每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode

  如果需要切换Mode,只能退出RunLoop,再重新指定一个Mode进入,这样做主要是为了分隔开不同组的Source、Timer、Observer,让其互不影响。如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出

注意:一种Mode中可以有多个Source(事件源,输入源,基于端口事件源例键盘触摸等)

    Observer(观察者,观察当前RunLoop运行状态)和Timer(定时器事件源)。但是必须至少有一个Source或者Timer,因为如果Mode为空,RunLoop运行到空模式不会进行空转,就会立刻退出。

###九. 系统默认注册的5个Mode:

####1.RunLoop 有五种运行模式,其中常见的有1.2两种

1. kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行

2. UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

3. UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用,会切换到kCFRunLoopDefaultMode

4. GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到

5. kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode

####2.Mode间的切换

我们平时在开发中一定遇到过,当我们使用NSTimer每一段时间执行一些事情时滑动UIScrollView,NSTimer就会暂停,当我们停止滑动以后,NSTimer又会重新恢复的情况,我们通过一段代码来看一下

    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

    {

        // [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];

        NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];

        // 加入到RunLoop中才可以运行

        // 1. 把定时器添加到RunLoop中,并且选择默认运行模式NSDefaultRunLoopMode = kCFRunLoopDefaultMode

        // [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

        // 当textFiled滑动的时候,timer失效,停止滑动时,timer恢复

        // 原因:当textFiled滑动的时候,RunLoop的Mode会自动切换成UITrackingRunLoopMode模式,因此timer失效,当停止滑动,RunLoop又会切换回NSDefaultRunLoopMode模式,因此timer又会重新启动了

        // 2. 当我们将timer添加到UITrackingRunLoopMode模式中,此时只有我们在滑动textField时timer才会运行

        // [[NSRunLoop mainRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

        // 3. 那个如何让timer在两个模式下都可以运行呢?

        // 3.1 在两个模式下都添加timer 是可以的,但是timer添加了两次,并不是同一个timer

        // 3.2 使用站位的运行模式 NSRunLoopCommonModes标记,凡是被打上NSRunLoopCommonModes标记的都可以运行,下面两种模式被打上标签

        //0 : {contents = "UITrackingRunLoopMode"}

        //2 : {contents = "kCFRunLoopDefaultMode"}

        // 因此也就是说如果我们使用NSRunLoopCommonModes,timer可以在UITrackingRunLoopMode,kCFRunLoopDefaultMode两种模式下运行

        [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

        NSLog(@"%@",[NSRunLoop mainRunLoop]);

    }

    -(void)show

    {

        NSLog(@"-------");

    }

由上述代码可以看出,NSTimer不管用是因为Mode的切换,因为如果我们在主线程使用定时器,此时RunLoop的Mode为kCFRunLoopDefaultMode,即定时器属于kCFRunLoopDefaultMode,那么此时我们滑动ScrollView时,RunLoop的Mode会切换到UITrackingRunLoopMode,因此在主线程的定时器就不在管用了,调用的方法也就不再执行了,当我们停止滑动时,RunLoop的Mode切换回kCFRunLoopDefaultMode,所以NSTimer就又管用了

  使用GCD也可是创建计时器,而且更为精确我们来看一下代码

####3. CFRunLoopSourceRef事件源(输入源)

    Source分为两种

        Source0:非基于Port的 用于用户主动触发的事件(点击button 或点击屏幕)

        Source1:基于Port的 通过内核和其他线程相互发送消息(与内核相关)

####4. CFRunLoopObserverRef

CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变

        -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

        {

            //创建监听者

            /*

            第一个参数 CFAllocatorRef allocator:分配存储空间 CFAllocatorGetDefault()默认分配

            第二个参数 CFOptionFlags activities:要监听的状态 kCFRunLoopAllActivities 监听所有状态

            第三个参数 Boolean repeats:YES:持续监听 NO:不持续

            第四个参数 CFIndex order:优先级,一般填0即可

            第五个参数 :回调 两个参数observer:监听者 activity:监听的事件

            */

            /*

            所有事件

            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

            };

            */

            CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {

            switch (activity) {

                case kCFRunLoopEntry:

                    NSLog(@"RunLoop进入");

                break;

                case kCFRunLoopBeforeTimers:

                    NSLog(@"RunLoop要处理Timers了");

                break;

                case kCFRunLoopBeforeSources:

                    NSLog(@"RunLoop要处理Sources了");

                break;

                case kCFRunLoopBeforeWaiting:

                    NSLog(@"RunLoop要休息了");

                break;

                case kCFRunLoopAfterWaiting:

                    NSLog(@"RunLoop醒来了");

                break;

                case kCFRunLoopExit:

                    NSLog(@"RunLoop退出了");

                break;

            default:

            break;

            }

        });

        // 给RunLoop添加监听者

        /*

        第一个参数 CFRunLoopRef rl:要监听哪个RunLoop,这里监听的是主线程的RunLoop

        第二个参数 CFRunLoopObserverRef observer 监听者

        第三个参数 CFStringRef mode 要监听RunLoop在哪种运行模式下的状态

        */

        CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);

        /*

        CF的内存管理(Core Foundation)

        凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release

        GCD本来在iOS6.0之前也是需要我们释放的,6.0之后GCD已经纳入到了ARC中,所以我们不需要管了

        */

        CFRelease(observer);

    }

Observer确实用来监听RunLoop的状态,包括唤醒,休息,以及处理各种事件

###十. RunLoop处理逻辑流程图

网址 `https://www.jianshu.com/p/de752066d0ad`

Source0

触摸事件处理

performSelector:onThread:

Source1

基于Port的线程间通信

系统事件捕捉

Timers

NSTimer

performSelector:withObject:afterDelay:

Observers

用于监听RunLoop的状态

UI刷新(BeforeWaiting)

Autorelease pool(BeforeWaiting)

01、通知Observers:进入Loop

02、通知Observers:即将处理Timers

03、通知Observers:即将处理Sources

04、处理Blocks

05、处理Source0(可能会再次处理Blocks)

06、如果存在Source1,就跳转到第8步

07、通知Observers:开始休眠(等待消息唤醒)

08、通知Observers:结束休眠(被某个消息唤醒)

01> 处理Timer

02> 处理GCD Async To Main Queue

03> 处理Source1

09、处理Blocks

10、根据前面的执行结果,决定如何操作

01> 回到第02步

02> 退出Loop

11、通知Observers:退出Loop

###十一. RunLoop应用

线程保活网址 `https://juejin.im/post/5c8cba495188257e16048237`

###App启动优化

https://juejin.im/post/5cff0ada6fb9a07edc0b4c3c

你可能感兴趣的:(RunLoop浅析)