美文网首页
Runloop详解

Runloop详解

作者: code_牧轩 | 来源:发表于2021-01-11 14:19 被阅读0次

    一、概念


     runloop 程序在运行过程中循环的做一些事情;

    二、 作用


    处理下面的事件:

               定时器

               GCD

               事件相应识别,页面刷新

               网络请求

               atuoReleasPool

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

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

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

    三.具体操作:


    UIApplicationMain  创建runloop;已经main函数的runloop

     伪代码如下:

             int retVal = 0;

             do {

               //睡眠中等待消息

                 int message = sleep_and_wait();

     //        处理消息

                 retVal = perccess_message(message);

             } while (retVal ==0);

             return 0;

     四:获取runloop的api


       NSRunLoop  是基于CFRunLoopRef的一层oc包装

       CFRunLoopRef 是开源的:https://opensource.apple.com/tarballs/CF/

    五、总结


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

     2.RunLoop保存在一个全局的dictinary里面,线程作为key,RunLoop作为value

     3.线程刚创建时并没有runloop对象,runloop会在第一次获取它时创建

     4.runloop会在线程结束时销毁

     5.主线程的runloop已经自动获取创建,子线程默认没有开启runloop

    从底层代码分析看出来:

     if (!loop) {

      CFRunLoopRef newLoop = __CFRunLoopCreate(t);

          __CFLock(&loopsLock);

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

     在获取runloop的如果没有runloop,会创建一个new的runloop对象,并且从  loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));

    这句看,是把线程作为key,Runloop作为value存在字典里面去,

    在子线程开启:

                [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];

                    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

    六 、runloop底层结构分析

    runloop对象的基本结构 跟runloop相关的5个类:

     CFRunLoopRef

     CFRunLoopModeRef

     CFRunLoopsourceRef

     CFRunLoopTimerRef

     CFRunLoopObserverRef

     struct __CFRunLoop {

     =========关键的5个成员变量

          pthread_t _pthread;

          CFMutableSetRef _commonModes;

          CFMutableSetRef _commonModeItems;

          CFRunLoopModeRef _currentMode;

          CFMutableSetRef _modes;

     };

     struct __CFRunLoopMode {

         CFStringRef _name;

         CFMutableSetRef _sources0;

         CFMutableSetRef _sources1;

         CFMutableArrayRef _observers;

         CFMutableArrayRef _timers;

     };

    从底层结构看出来:

    1)、结构分析:

     1.CFRunLoopModeRef代表runloop的运行模式;

     2.一个runloop包含若干个Mode,每个Mode又包含若干个soure0/soure1/timer/ObserVer;

     3.runloop启动时只能选择其中一个mode,作为CurrentMode

     4.如果需要切换mode,只能退出当前Loop,再重新选择一个Mode进入;

         这样的作用是将不同组的source0/source1/Timer/Observer能分隔开来,互不影响

     5.如果mode里面没有任何soure0/soure1/Timer/Observer,runloop会立即退出;

        注:此处的退出并不是runloop退出while循环,不会导致程序退出,而是,在while循环里面进行model的切换;

    2)、mode的类型有

    kCFRunLoopDefaultMode;NSDefaultRunLoopMode;APP的默认mode,通常主线程是在这个mode下运行;

     UITrackingRunLoopMode;  滚动model  一帮常见的是这2种模式;界面跟踪Mode,用于scrollView追踪触摸滑动,保证页面滑动时不受其他Mode影响;

     kCFRunLoopCommonModes  默认包括kCFRunLoopDefaultMode、UITrackingRunLoopMode;2种模式都兼容

    //    UITrackingRunLoopMode\NSDefaultRunLoopMode才是真正存在的模式

    //    NSRunLoopCommonModes并不是一个真正的模式,而是一个标记;

    //    timer在设置了common标记的模式下都可以运行;

    GSEventReceiveRunLoopMode

    3)、modle内部参数说明

    source0

            1.触摸事件处理

            2.performSelector:onThread:

        source1

           1.基于Port的线程间通信;

           2.系统事件捕捉,(先通过soure1处理,然后分发到soure0来处理);

          Timers

            [self performSelector:@selector(playInputClick) withObject:self afterDelay:2];

          Obervers

            用于监听RunLoop的状态;(休眠或者唤醒)

            UI刷新(BeforeWaiting)

            Autorelease Pool

    七:Obervers  自己添加


             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),            // 即将退出Loop

                 kCFRunLoopAllActivities = 0x0FFFFFFFU

             };

    方法一:

        CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopAllActivities, YES, 0, observeRunLoopActicities, NULL);

        CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);

        CFRelease(observer);

    方法二:

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

            switch(activity) {

                case kCFRunLoopEntry:

                    NSLog(@"kCFRunLoopEntry");

                    break;

                case kCFRunLoopBeforeTimers:

                    NSLog(@"kCFRunLoopBeforeTimers");

                    break;

                case kCFRunLoopBeforeSources:

                    NSLog(@"kCFRunLoopBeforeSources");

                    break;

                case kCFRunLoopBeforeWaiting:

                    NSLog(@"kCFRunLoopBeforeWaiting");

                    break;

                case kCFRunLoopAfterWaiting:

                    NSLog(@"kCFRunLoopAfterWaiting");

                    break;

                casekCFRunLoopExit:

                    NSLog(@"kCFRunLoopExit");

                    break;

                default:

                    break;

            }

        });

        CFRunLoopAddObserver(CFRunLoopGetMain(), observer1, kCFRunLoopCommonModes);

        CFRelease(observer1);

    //    处理block的逻辑

        CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{

        });

    八 、runloop底层原理分析

    1)、入口函数

     SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {

     //通知Oberservers进入loop

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

     //  具体要做的事情

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

     //    通知Oberserver:退出Loop

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

         return result;

     }

    2)、__CFRunLoopRun内部函数分析

     SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {

     //通知Oberservers进入loop

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

     //  具体要做的事情

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

     //    通知Oberserver:退出Loop

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

         return result;

     }

     __CFRunLoopRun这个函数是loop的关键处理的代码逻辑

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

         int32_t retVal = 0;

         do {

     // 通知observers:即将处理timer

             __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);

     // 通知observers:即将处理Sources

            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

     //处理block;

         __CFRunLoopDoBlocks(rl, rlm);

     //处理soure0

         if ( __CFRunLoopDoSources0(rl, rlm, stopAfterHandle)) {

             //处理block;

                 __CFRunLoopDoBlocks(rl, rlm);

            }

     //判断有没有soure1

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

     //        如果有soure1,就跳转到handle_msg

                     goto handle_msg;

                 }

     // 通知OberVers:即将休眠

        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);

         __CFRunLoopSetSleeping(rl);

     //  等待别的消息来唤醒当前线程

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

             __CFRunLoopUnsetSleeping(rl);

     //  通知observe:休眠

            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

     handle_msg:;

            if (被timer唤醒) {

     //          处理timer

                __CFRunLoopDoTimers(rl, rlm, mach_absolute_time())

            }

             else if (被GCD唤醒) {

     //          处理GCD

                 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);

             } else { //被soure1唤醒

     //        处理soure1

              __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;

             }

     //再次处理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;

     }

    3)、休眠函数分析

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

    休眠是线程阻塞,cup不会给它分配资源,代码不会往下执行;当前线程休息,CUP也休息;用户态切换到内核态;

     具体的实现:

     mach_msg();直接sleep;内核层面的休息;

     用户态                                  内核态

      mach_msg()------->    mach_msg()

     等待消息

     没有消息就让当前线程休息

     有消息让线程唤醒

     whiel(1);这也是一种阻塞,当前线程没有休息还是在执行,CUP没有休息;

    总结如图所示:

    图1-1

    九、实际应用场景

        1.控制线程生命周期(AFNetWorking)

        2.解决NStimer在滑动时停止工作问题

        3.性能优化,

        4.监控应用卡顿

    相关文章

      网友评论

          本文标题:Runloop详解

          本文链接:https://www.haomeiwen.com/subject/btkdaktx.html