Gotta Go Fast:在iOS中构建速度。 第1部分

有许多技巧可以使iOS开发人员知道如何进行性能优化,以使应用程序中的动画流畅运行。 阅读本文之后,您将了解iOS开发人员意味着16.67毫秒的时间,以及使用哪种工具更好地跟踪代码。

本文基于Fyusion的iOS工程师,RayWenderlich.com上的iOS开发教程作者Luke Parham在国际移动开发者大会MBLT DEV 2017上发表的主题演讲。

“大家好。 假设可以,您可以将启动时间缩短10秒,再将其乘以500万用户,那么每天就是5000万秒。 一年多的时间可能是数十年。 因此,如果使其启动速度加快十秒钟,那么您可以挽救十几条生命。 那真的很值得,你不觉得吗?”

史蒂夫·乔布斯(Steve Jobs)谈性能(Apple II的启动时间)。

主线程负责接受用户输入并将结果显示在屏幕上。 接受点击,平移,所有手势,然后进行渲染。 大多数现代手机的渲染速度为每秒60帧。 这意味着每个人都希望在16.67毫秒内完成所有工作。 因此,摆脱主线程是一件非常大的事情。

如果花费的时间超过16.67毫秒,那么您将自动丢帧,并且在播放动画时用户会看到它。 有些设备的渲染时间甚至更少,例如,新的iPad具有120赫兹的频率,因此每帧仅需要8毫秒来完成工作。

规则1:使用CADisplayLink跟踪丢帧

CADisplayLink是在Vsync上触发的特殊计时器。 Vsync是应用程序渲染到屏幕时的时间,它每16毫秒发生一次。 出于测试目的,您可以在AppDelegate中设置添加到主运行循环中的CADisplayLink,然后只需执行其他函数即可进行一些数学运算。 然后,您跟踪该应用程序已运行了多长时间,以及自上次启动此功能以来已经运行了多长时间。 并查看是否花费了超过16毫秒的时间。

只有在实际渲染时才会触发。 如果您正在做大量工作,并且减慢了主线程的速度,那么它将在100毫秒后运行,这意味着您做了太多的工作,并且在那段时间内丢了帧。

例如,这是应用程序Catstagram。 图片加载时会结结巴巴。 然后您可以看到该帧在某个时间被丢弃,经过的时间大约为200毫秒。 这意味着该应用正在执行耗时太长的操作。

用户不喜欢这种体验,特别是如果该应用程序支持iPhone 5,旧iPod等旧设备。

Time Profiler可能是追踪这些内容的最有用的工具。 其他工具也很有用,但最终,在Fyusion中,我们有90%的时间使用了Time Profiler。 该应用程序的常见可疑对象是滚动视图,文本和图像。

图像是非常重要的。 我们有JPEG解码-“ UIImageView”等于一些UIImage。 UIimage解码该应用程序的所有JPEG。 他们做的很慢,所以您不能真正直接跟踪性能。 当您设置图像时,它不会正确发生,但是您可以在时间分析器跟踪中看到它。
文本测量是另一件事。 它确实出现了,例如,如果您有很多非常复杂的语言,例如日语或中文。 这些可能需要很长时间才能进行线路测量。

层次结构布局还会减慢应用程序的渲染速度。 对于自动版式尤其如此。 与手动布局相比,它很方便,但速度也很慢。 因此,这是这些折衷之一。 如果它减慢了应用程序的速度,则可能是时候放弃它并尝试其他一些布局技术了。

在示例调用树中,您可以看到CPU正在进行多少工作。 您可以切换视图,按线程查看视图,按CPU查看视图。 通常,最有趣的事情是通过线程分开,然后查看主要线程。

很多时候,当您第一次开始查看此内容时,它似乎超级压倒性的。 有时您会感到:“这些垃圾是什么? 我不知道这是什么意思“СFRunLoopDoSource0”。

但这是您可以深入研究并了解其工作原理的一件事,并且它开始变得有意义。 因此,您可以遵循堆栈跟踪并查看所有未编写的系统内容。 但是在底部,您可以看到实际的代码。

例如,我们有一个非常简单的应用程序,它具有main函数,然后在main函数内部调用了一些方法。 探查器的时间是默认情况下每毫秒对当前堆栈跟踪的快照进行快照。 然后等待一毫秒并进行快照,在此您已将“ main”称为“ foo”,将其称为“ bar”。 屏幕截图上有第一个堆栈跟踪。 这样就可以收集了。 我们有这些计数:1、1、1。

这些功能中的每一个都被调用一次。 然后一毫秒后,我们捕获了另一个堆栈。 这次完全一样,我们将所有计数加2。

然后在第三毫秒,我们有一个稍微不同的调用堆栈。 Main直接调用“酒吧”。 主和酒吧上升了一个。 但是,然后我们分开了。 有时主要调用“ foo”,有时主要调用直接“ bar”。 那只发生一次。 一种方法已在另一种方法内调用。

此外,在另一个内部调用了一个方法,该方法调用了第三个方法。 我们看到“ buz”被叫了两次。 但这是一种很小的方法,它发生在一毫秒之间。

使用时间分析器,重要的是要记住它没有给出确切的时间。 它不能确切说明一种方法需要花费多长时间。 它告诉它在快照中出现的频率,它只能近似地估计每个方法执行的时间。 因为如果某事足够短,它将永远不会出现。

如果在调用树中切换到控制台模式,则可以看到所有丢帧事件,并且可以将它们匹配。 我们丢了很多帧,并且正在进行很多工作。 您可以放大时间分析器,并仅在此部分中查看正在执行的操作。

实际上,通常在Mac中,您可以选择单击显示三角形,它会神奇地打开并向您显示其中最重要的内容。 它会归结为大部分工作。 90%的时间是CFRunLoopRun,然后是回调。

整个应用程序基于运行循环。 您有一个永远存在的循环,然后在循环的每次迭代中都将调用回调。 到此为止,您可以深入研究每个方面,并基本上了解一下您的前三个或四个瓶颈。

如果我们深入研究其中的一种,就可以在真正容易看到的地方看到这些东西,就像:“哇,我不知道它在做什么。”就像IO的渲染器一样。

有一个选项可以隐藏系统库。 隐藏确实很诱人,但实际上,这实际上是应用程序中的最大瓶颈。

权重表明该特定功能或方法正在执行的工作百分比。 而且,如果我们深入研究该示例,则有34%的情况是由于Apple jpeg_decode_image_all而发生的。 经过一些研究,很明显,这意味着JPEG解码正在主线程上进行,并导致大多数帧丢失。

通常,最好在后台解码JPEG。 大多数第三方库(AsyncDisplayKit,SDWebImage等)都是开箱即用的。 如果您不想使用框架,则可以自己完成。 您要做的是传入图像,在这种情况下,它是UIImage的扩展,然后设置上下文,然后将图像手动绘制到CGBitmap中的上下文中。

当您这样做时,可以从后台线程调用解码的Image()方法。 这将始终返回解码后的图像。 无法检查特别是UIImage是否已经解码,因此您始终必须在此处传递它们。 但是,如果您正确地缓存内容,它不会做任何额外的工作。

这样做在技术上效率较低。 使用UIimageView是超级优化的,超级高效的。 它将进行硬件解码,因此这是一个折衷。 您的图像将以这种方式解码得更慢。 但是,好事是您可以调度到后台队列,使用我们刚刚看到的方法解码图像,然后跳回到主线程并设置您的内容。

即使该工作花费了更长的时间,也可能不会在主线程上发生,所以它没有阻止用户交互,因为它没有阻止滚动。 所以这是一个胜利。

任何出现内存迹象的警告都会删除所有内容,请删除所有未使用的内存。 但是,如果您在后台线程上发生了事情,则分配这些大的已解码JPEG会占用后台线程大量的新内存。

这发生在Fyuse应用程序中。 如果我跳到后台线程,解码所有JPEG,在某些情况下,例如在旧手机上,系统会立即将其杀死。 那是因为它发出了内存警告,说:“嘿! 摆脱记忆”,但后台队列不监听。 如果您分配所有这些图像,然后每次崩溃,将会发生什么情况。 解决此问题的方法是从后台线程ping主线程。

通常,主线程是一个队列。 事情排队,并发生在主线程上。 在Objective-C中进入后台时,可以使用performSelectorOnMainThread:withObject:waitUntilDone:。 这会将其放在主队列行的末尾,因此,如果主队列正忙于处理内存警告,则此函数调用将转到该行的末尾,并等待所有内存警告得到处理,然后再进行所有此类的繁重分配。记忆。

在Swift中,它更简单。 您可以在main上同步执行一个dispatch main空块。

这是一个示例,其中我们已经清理了东西,并且正在对后台队列进行图像解码。 视觉上的滚动更漂亮。 我们仍然有丢帧的情况,但这是在iPod 5g上,因此这是您可以在仍支持iOS 10和11的情况下测试的最糟糕的事情之一。

当这些框掉落时,您可以继续寻找。 仍有正在发生的工作并导致这些帧丢失。 您可以做更多的事情来使其更快。

总结起来,这并不总是那么容易,但是如果您有一些小事情需要花费很多时间,则可以在后台进行。

确保它与UIKit不相关。 许多UIKit类不是线程安全的,因此您不能在后台分配该UIView。

如果您需要在后台进行图像处理,请使用Core Graphics。 不要隐藏系统库。 并且不要忘记内存警告。

这是基于Luke Parham的介绍的文章的第一部分。 如果您想了解有关UI在iOS中的工作方式的更多信息,为什么要使用贝塞尔曲线路径以及何时需要使用手动内存管理,请继续关注! 我们将很快发布第二部分。

2018年MBLT DEV会议将于2018年9月28日在莫斯科举行。再有20多位专家将分享他们的专业知识,谈论他们实现的有用工具以及讨论Android和iOS中的最新技术更新。

您有什么要分享的吗? 加入会议阵容:在6月30日之前填写CFP并获得资助。

还没准备好发表演讲吗? 加入会议参加者以提高技能买票