iOS 应用,丝般顺滑的理想情况就是 60FPS (对于 iPad Pro 是 240FPS),即在 16ms 之内完成一次渲染。如果找到在每次渲染花费了多久,究竟做了什么事情,那么就可以进行针对性的优化。

RunLoop 的概念

在程序中,我们需要一种机制,可以让当前线程能够随时处理事件但不退出。这种模型通常被称为 Event Loop,在 iOS 中使用 RunLoop 来实现。
RunLoop 管理了所在线程需要处理的事件和消息。当有事情需要处理时,就唤醒当前线程,处理事件。当事情全部处理完毕时,线程处于休眠状态,以避免资源占用。这样子线程一直处于“接受消息->等待->处理”循环中。

根据苹果文档,RunLoop 的内部逻辑如上。RunLoop 有很多种状态,比如 beforeWaitingafterWaiting,当状态发生改变时,就会通知 observer。而触摸事件、定时器、网络请求返回等都是作为 Source0、Source1、Timer 被加到需要处理的队列中,都可以唤醒当前线程的 RunLoop。

RunLoop 与界面更新

当在操作 UI 时,比如改变了 Frame、更新了 UIView/CALayer 的层次时,或者手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,这个 UIView/CALayer 就被标记为待处理,并被提交到一个全局的容器去。

苹果注册了一个 Observer 监听 BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) 事件,回调去执行一个很长的函数:
_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。这个函数里会遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。

即界面更新一定发生在主线程的 RunLoop BeforeWaiting 状态发生时。那么我们可以得出一个结论,如果主线程的 RunLoop 当前事件循环占用的时间过多,超过了 32 ms,那么一定会发生一次掉帧。

Point of Interest

iOS 10 之后,引入了一个新的 API,kdebug_signpost_startkdebug_signpost_end。可以结合 Instrument 的 Point of Interest 功能,用来分析起始到结束的耗时。

//事件段的起始和结束
// Timing an activity (code 7 - "Start Up")
kdebug_signpost_start(7, 0, 0, 0, 0);
[self loadAssets];
kdebug_signpost_end(7, 0, 0, 0, 0);

具体使用方法如上,其中第一个参数用来标识事件的 ID,可以同时分析多个事件。如果某个事件发生了多次,那么就可以得到一个列表,以及这个事件耗时的统计信息。

选择列表中的某个点以后,右键选择,可以过滤掉其他时间的信息。

这样子,结合 Time Profile 功能,就可以看到某个事件耗时,以及究竟哪些代码耗时。

结合 RunLoop 与 Point of Instrument 功能

为主线程的 RunLoop 增加一个 observer,并监听特定的状态变化,就找出每一个 RunLoop 循环究竟花费了多长事件。

使用 Instrument,可以得到下面的图。

这样子可以看到每一个 RunLoop 耗时多少,耗时在哪里。找出 Top 问题,针对性优化。

System Trace

time profile 只是查看 CPU 的执行情况,如果一个线程长时间得不到调度,在 time profile 里得不到相应的信息。这时需要用到 System Trace 这个工具。
使用 system trace 时,会记录最近 5s 的 kernel trace,然后分析 Scheduling activity、System calls、Virtual memory operations 等信息。如果可以卡顿可以复现,那么就可以找出来锁等待、死锁、系统调用造成的卡顿问题。
如下图就是由于线程调度造成的卡顿问题。可以看到主线程被 block 了 1 秒多,原因是调用了 AudioSession 相关的函数。

利用 RunLoop 进行优化

找到每一个 RunLoop 中耗时之后,就可以针对性优化,比如主线程读写、懒加载、异步布局之类。也可以把比较复杂的任务分解到不同的 RunLoop 中,这样子 RunLoop 循环的时间不会太长,可以更快响应事件。
具体做法可以参考 texture 这个组件。下面是 copy 过来的代码。

  1. 为主线程的 RunLoop 增加一个 Source

    _runLoopSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext);
    CFRunLoopAddSource(runloop, _runLoopSource, kCFRunLoopCommonModes);
  2. 如果需要,把事件加入一个队列中,等待下一个 RunLoop 处理

      [renderQueue enqueue:node];
  3. Runloop 进入 kCFRunLoopBeforeWaiting 状态时,判断队列中是否有待处理的事情。如果有,唤醒 Source,使得 RunLoop 马上进入下一个时间循环

    if (!isQueueDrained) {
    CFRunLoopSourceSignal(_runLoopSource);
    CFRunLoopWakeUp(_runLoop);
    }

参考

结合 RunLoop 和 Instrument 定位卡顿的更多相关文章

  1. UE手游如何应对CPU帧率瓶颈和卡顿?

    如何高效准确详细的对性能进行剖析?腾讯游戏学院专家Leonn将归纳总结在UE下对每一性能指标的剖析方法,本文重点讲解如何应对CPU帧率瓶颈和卡顿? CPU上帧率低和卡顿是性能优化中最易出现的一部分,尤 ...

  2. APP测试工具之TraceView卡顿检测

    Traceview卡顿检测 Traceview是Android平台特有的数据采集和分析工具,集成在DDMS工具中,可以采集程序中的方法执行耗时.调用关系.调用次数以及资源占用等情况. 一.使用方法 1 ...

  3. 想让安卓app不再卡顿?看这篇文章就够了

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由likunhuang发表于云+社区专栏 实现背景 应用的使用流畅度,是衡量用户体验的重要标准之一.Android 由于机型配置和系统的 ...

  4. iOS 性能优化总结

    卡顿产生的原因 在 VSync信号到来后,系统图形服务会通过 CADisplayLink等机制通知 App,App主线程开始在 CPU中计算显示内容,比如视图的创建.布局计算.图片解码.文本绘制等.随 ...

  5. 80.Android之内存管理

    转载:http://www.jianshu.com/p/9fb0a795da93 1. Android中的内存 1.1 Android中的垃圾回收机制 Android 平台最吸引开发者的一个特性:有垃 ...

  6. 【行业交流】2016 TiD质量竞争力大会——移动互联网测试到质量的转变之路

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/5691233.html TiD质量大会在北京召开,有幸去参加 ...

  7. android app性能优化大汇总(UI渲染性能优化)

    UI性能测试 性能优化都需要有一个目标,UI的性能优化也是一样.你可能会觉得“我的app加载很快”很重要,但我们还需要了解终端用户的期望,是否可以去量化这些期望呢?我们可以从人机交互心理学的角度来考虑 ...

  8. Android UI性能优化详解

    设计师,开发人员,需求研究和测试都会影响到一个app最后的UI展示,所有人都很乐于去建议app应该怎么去展示UI.UI也是app和用户打交道的部分,直接对用户形成品牌意识,需要仔细的设计.无论你的ap ...

  9. Python爬取CSDN博客文章

    0 url :http://blog.csdn.net/youyou1543724847/article/details/52818339Redis一点基础的东西目录 1.基础底层数据结构 2.win ...

随机推荐

  1. sd卡不能格式化

    可能是读卡器坏了,还真遇到过,花了一下午,各种尝试,最后发现只是读卡器坏了.

  2. 为什么c++中返回成员变量的指针,会破坏了封装?

    上述代码中,get()函数返回的是类成员变量的name的地址,这是很危险的,name是私有的,意味这不想被客户访问,但是如果返回name的地址,那么外部函数就可以修改name,这就破坏了封装性. 为什 ...

  3. C语言基础课第四次作业

    1.实验代码      7-2 打印九九口诀表 (15 分) #include<stdio.h> #include<math.h> int main(void){ int a, ...

  4. 卡特尔16PF性格测试与答案

    大学生在职业生涯规划时,必须充分注意到自己的性格和职业的适宜性.性格是指一个人在生活中形成的对现实的稳定的态度和行为方式.研究表明,性格影响着一个人的职业取向,由于性格的不同,每个人对工作和职业的态度 ...

  5. MyEclipse配置Maven插件

    一.工具环境 1.jdk-7u80-windows-x64 2.apache-tomcat-7.0.70 3.apache-maven-3.3.9 4.MyEclipse 10.7 5.windows ...

  6. Redis总结和提取常用的和重要的命令

    一:Redis的结构和其数据类型(注redis默认端口号是6379) 1)Redis可以部署多套(多个进程不同端口或直接部署在不同主机),每个Redis都可以有多个db,通过select来选择,默认的 ...

  7. SpringMVC零碎笔记

    在web.xml里可以配置webapp的默认首页,格式如下: <welcome-file-list> <welcome-file>index.html</welcome- ...

  8. (线段树 && 字符串的处理)codeforces -- 570C

    链接: http://acm.hust.edu.cn/vjudge/contest/view.action?cid=87813#problem/J Description Daniel has a s ...

  9. Oracle EBS - Setup: 配置文件Profile

    http://blog.csdn.net/lfl6848433/article/details/8696939 Oracle EBS - Setup: 配置文件Profile 1.诊断Diagnost ...

  10. eclipse/myeclipse介绍

    eclipse更加纯净,比较简洁,需要某些插件的时候,需要自己去配置才可以,而myeclipse自带了很多的插件功能更为强大. 在eclipse于myeclipse创建的项目是有差异的,eclipse ...