RunLoop下的卡顿监控

作者: o0阿拉斯加的狗0o | 来源:发表于2016-12-06 21:55 被阅读168次

    在开发中,我们可以使用Xcode自带的Instruments工具的Core Animation来对APP运行流畅度进行监控,使用FPS这个值来衡量。这个工具我们只能知道哪个界面会有卡顿,无法知道到底是什么操作哪个函数导致的卡顿。

    屏幕快照 2016-12-06 下午10.09.40.png

    界面出现卡顿,一般是下面几种原因:

    • 主线程做大量计算
    • 主线程大量的I/O操作
    • 大量的UI绘制
    • 主线程进行网络请求以及数据处理
    • 离屏渲染

    监控界面卡顿,主要是监控主线程做了哪些耗时的操作,之前的文章中已经分析过,iOS中线程的事件处理依靠的是RunLoop,正常FPS值为60,如果单次RunLoop运行循环的事件超过16ms,就会使得FPS值低于60,如果耗时更多,就会有明显的卡顿。

    正常RunLoop运行循环一次的流程是这样的:

    SetupThisRunLoopRunTimeOutTimer();
    do {
            __CFRunLoopDoObservers(kCFRunLoopBeforeTimers);
            __CFRunLoopDoObservers(kCFRunLoopBeforeSources);
      
            __CFRunLoopDoBlocks();
            __CFRunLoopDoSource0(); // 处理source0事件,UIEvent事件,比如触屏点击
    
            CheckIfExitMessagesInMainDispatchQueue(); // 检查是否有分配到主队列中的任务
    
            __CFRunLoopDoObservers(kCFRunLoopBeforeWaiting);
            var wakeUpPort = SleepAndWaitForWakingUpPorts(); // 开始休眠,等待ma ch_msg事件
            
            // mach_msg_trap
            // ZZz.....   sleep
            // Received mach_msg,  wake up
            
            __CFRunLoopDoObservers(kCFRunLoopAfterWaiting); // 被事件唤醒
            // Handle msgs
            if (wakeUpPort == timePort) { // 被唤醒的事件是timer
                  __CFRunLoopDoTimers(); 
            } else if (wakePort == mainDispatchQueuePort) { // 主队列有调度任务
                  __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__();
            } else { // source1事件,UI刷新,动画显示
                  __CFRunLoopDoSource1();
            }
            __CFRunLoopDoBlocks();
    } while (!stop && !timeout)
    
    

    从这个运行循环中可以看出,RunLoop休眠的事件是无法衡量的,处理事件的部分主要是在kCFRunLoopBeforeSources之后到kCFRunLoopBeforeWaiting之前kCFRunLoopAfterWaiting 之后和运行循环结束之前这两个部分

    监控这两个部分的耗时,使用CFRunLoopObserverRef来监控RunLoop的状态:

    创建信号量&获取Runloop的状态

    使用信号量dispatch_semaphore来控制对RunLoop状态判断的节奏,这个可以保证,每个RunLoop状态的判断都会进行。
    对RunLoop状态的判断,我们专门在另外一个线程做判断。

    需要注意的是,对卡顿的判断是通过kCFRunLoopBeforeSources或者kCFRunLoopBeforeWaiting这两个状态开始后,信号量+1,这时候信号量>0,dispatch_semaphore_wait不会阻塞,返回0,进行下一个while循环,如果此时还没有进入下一个RunLoop状态,此时信号量=0,dispatch_semaphore_wait就会在这里阻塞,到了设定的超时时间,dispatch_semaphore_wait的返回值>0,这时候就会进行耗时的判断。
    我们可以自己设定超时时间和超过多少次算卡顿,这里设置超过250ms。

    子线程监控

    相关文章

      网友评论

      • 377841418262:请问下为什么认为进入kCFRunLoopAfterWaiting和kCFRunLoopBeforeSources就会认为是卡顿?

      本文标题:RunLoop下的卡顿监控

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