美文网首页
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.监控应用卡顿

相关文章

  • iOS Runtime

    iOS RunLoop详解---重要而详细iOS RunLoop详解-部分

  • RunLoop详解

    RunLoop详解 RunLoop运行循环(死循环) RunLoop模式 NSDefaultRunLoopMode...

  • Runloop 详解

    Runloop 详解 参考链接: 深入理解RunLoop CFRunLoop 概念 runloop :是管理和处理...

  • Runloop

    Runloop 实现原理及应用iOS - RunLoop 底层源码详解及具体运用

  • 深入理解RunLoop

    深入理解runloopiOS RunLoop详解

  • 记录一些介绍Runloop的牛文

    老司机出品——源码解析之RunLoop详解深入理解RunLoop关于RunLoop部分源码的注释CFRunLoop...

  • 底层原理探究(二)RunLoop

    转自: 老司机出品——源码解析之RunLoop详解入门使用: RunLoop入门 看我就够了孙源的Runloop视...

  • RunLoop 详解

    关于 runloop 的运行 点击链接详解附有源码

  • iOS RunLoop 详解

    转自 iOS RunLoop 详解 image.png Runloop 是和线程紧密相关的基础组件,是很多多线程有...

  • 详解RunLoop之面试题

    本文首发于个人博客 回顾详解RunLoop之源码分析中提出的问题 什么是Runloop ios程序中 main函数...

网友评论

      本文标题:Runloop详解

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