iOS中的性能或如何卸载主线程。 第一部分



当一项任务应在16.67毫秒内完成时,有许多技巧可以帮助优化iOS应用程序的工作。 我们告诉您如何卸载主线程,以及哪种工具更适合跟踪主线程中的调用堆栈。


“伙计们,让我们想象一下,您可以将启动时间减少10秒。 乘以500万用户,我们每天将有5000万秒。 一年中,这将相当于大约十个人的生命。 因此,如果将初始下载速度提高了10秒钟,您将挽救数十个人的生命。 真的值得,不是吗?

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


这篇文章基于Fyusion iOS开发人员Luc Parham的一份报告,他在去年的MBLT DEV国际移动开发人员大会上发表了讲话


MBLT DEV 2018将于9月28日在莫斯科举行。 门票现在最便宜。 按照惯例,在程序委员会选择报告时,您可以购买konf的早鸟票。 现在抓住这个机会。 从6月29日开始,门票将更加昂贵。


丢帧


主线程执行负责触摸类型事件并使用UI的代码。 他渲染屏幕。 大多数现代智能手机以每秒60帧的速度渲染。 这意味着任务必须在16.67毫秒(1000毫秒/ 60帧)中完成。 因此,主线程中的加速很重要。


如果某些操作花费的时间超过16.67毫秒,则会自动发生帧丢失,并且应用程序用户在播放动画时会注意到这一点。 在某些设备上,渲染甚至更快,例如,在iPad Pro 2017上,屏幕刷新率为120 Hz,因此在一帧中完成操作仅需8毫秒。


规则1


CADisplayLink是一个特殊的计时器,在垂直同步(Vsync)期间启动。 垂直同步可确保分配不超过16.67毫秒的帧渲染时间。 作为AppDelegate中的一项检查,您可以在主运行循环中注册CADisplayLink ,然后您将拥有一个附加功能来执行计算。 您可以跟踪应用程序的持续时间,并找出自上次启动此功能以来已经过了多少时间。



当出现需要渲染时启动。 如果执行了许多不同的操作使主线程超载,则此函数将以100毫秒的延迟开始。 这意味着完成了太多的工作,并且此时人员已经流失。


这是Catstagram应用程序。 下载图像时,应用程序开始变慢。 我们看到帧速率在某个点下降,并且加载时间持续了大约200毫秒。 似乎花费了太多时间。



用户不会对此感到满意,特别是如果该应用程序在旧设备(例如iPhone 5或旧iPod型号等)上运行时。


时间分析器


跟踪此类问题的有用工具是Time Profiler。 其他工具也很有用,但最终在Fyusion中,有90%的时间我们使用了Time Profiler。 通常,应用程序中的问题与ScrollView(带有文本和图像的区域)有关。


图像很重要。 我们使用UIImage解码JPEG格式。 他们做得很慢,我们无法直接跟踪他们的表现。 在UIImageView设置图像后,不会立即发生这种情况,但是您可以通过在Time Profiler中进行跟踪来看到这一刻。


文本格式是另一个重要点。 当应用程序具有大量“复杂”文本(例如日语或中文)时,这很重要。 计算带有文本的行的正确大小可能需要很长时间。


接口标记还会减慢应用程序中的渲染速度。 对于自动版式工具尤其如此。 AutoLayout易于使用,但是与手动标记相比,它大大降低了应用程序的速度。 我们必须让步。 如果AutoLayout减慢了应用程序的速度,则可能是时候放弃它并尝试其他类型的标记了。


痕迹图案




在此示例调用树中,您可以看到CPU进行的工作。 您可以更改跟踪的类型,从线程,处理器的角度来看它。 通常,最有趣的事情是将跟踪分为线程并监视主线程。


初始跟踪分析可能看起来很复杂。 并非总是能够立即弄清楚FRunLoopDoSource0含义。


遍历跟踪,您可以了解系统的工作原理,然后一切都变得有意义。 您可以跟踪堆栈跟踪并查看所有未编写的系统元素。 但最底层是您的源代码。


通话树


假设我们有一个非常简单的应用程序。 它包含调用其他几个函数的主要函数。 Time Profiler的工作本质是,它以一毫秒(默认)的频率拍摄堆栈跟踪的当前状态的快照。 再过一毫秒,他将跟踪记录快照。 它调用主函数,该函数调用函数“ foo ”,该函数调用函数“ bar ”。 初始堆栈跟踪显示在下面的屏幕快照中。 这些数据一起收集。 在每个功能的对面,均标有数字:1、1、1。




这意味着每个功能都被调用一次。 然后,在一毫秒后,我们又获得了堆栈的快照。 这次看起来完全一样,所以所有数字都加1,我们得到2、2、2。




在第三毫秒内,我们的调用堆栈看起来有些不同。 主要功能直接调用bar 。 因此,在主功能和“ bar ”功能上又增加了一个单位,它们的值变为3。接下来,发生分离。 有时主函数直接调用“ foo ”,有时直接调用“ bar ”。 这发生了一次。 一个功能通过另一个调用。


接下来,一个函数调用了另一个函数,后者又调用了第三个函数。 我们看到“ baz ”函数被调用了两次。 但是此功能微不足道,以至于它被称为比一毫秒还快。


使用Time Profiler时,请记住它没有显示特定的时间间隔,这一点很重要。 它不显示函数的确切执行时间。 他只报告图片中出现的频率,仅给出每个功能持续时间的近似值。 由于某些过程足够快,因此它们永远不会显示在图片上。




将呼叫切换到控制台模式时,您可以查看并比较所有降低帧速率的时刻。 在该示例中,帧丢失发生了几次,并且执行了各种处理。




单击alt并单击macOS,将展开部分和子部分,而不仅仅是所选部分。 它们将根据执行的工作量进行排序。 在90%的情况下, CFRunLoopRun ,然后是回调。


该应用程序完全基于单个“运行循环”任务执行周期。 有一个无休止的重复循环,每次迭代都会启动回调。 如果查看这些回调,则可以突出显示最主要的瓶颈。


更详细地研究了这些挑战之后,您很可能不了解它们在做什么。 这些可以是渲染,图像提供者,IO。




有一个选项可让您隐藏系统库。 它们实际上是应用程序的问题区域。


有些仪表以百分比的形式显示特定功能或操作执行的工作量。 如果看这个例子,我们将在这里看到值-34%。 这是Apple jpeg_decode_image_all进程。 经过研究,很明显JPEG图像的解码发生在主线程中,并且在大多数情况下,这是造成帧丢失的原因。




规则2


通常,应在后台对jpeg图像进行解码。 大多数第三方库(AsyncDisplayKit,SDWebImage等)默认情况下都可以执行此操作。 如果您不想使用框架,则可以手动进行解码。 为此,您可以在UIImage编写扩展,在其中创建上下文并手动绘制图像。




执行此操作时,可以从主线程而不是从主线程调用decodeImage函数。 它将始终返回解码的图像。 无法检查特定的UIImage图像是否已通过解码,因此您始终必须通过此方法将它们传递。 但是,如果您正确地缓存了数据,则系统中不会有不必要的进程。


从技术角度来看,这不太有效。 使用UIImageView类似乎是优化和高效的。 但是它也执行硬件解码,因此也有其缺点。 使用这种方法,您的图像将被解码得更慢。 但是有个好消息-您可以按照上述方式在主线程上解码图像,然后返回主线程并配置接口。




尽管此操作需要更多时间,但可能不会在主线程上执行该操作,这意味着它不会干扰应用程序中的用户活动,因为它不会减慢磁带的滚动速度。 有利可图的解决方案。


内存不足警报


对于任何内存不足的信号,我想删除所有可能的未使用数据。 但是,如果对第三方流执行各种处理,则将体积解码的JPEG图像放置在第三方流上将占用大部分可用空间。


Fyuse应用程序中发生了此类问题。 如果我已经在第三方流上解码了所有JPEG图像,则在某些情况下(例如在较旧的手机型号上),该系统会立即中断应用程序。 这可能是由于以下事实:第三方任务流未响应有关系统内存不足的警告,例如“嘿,删除不必要的数据!”。 发生以下情况:首先将所有这些图像放置在第三方流上,然后应用程序不断崩溃。 如果第三方线程向主线程发送有关系统中发生的事件的信号,则不会发生此类问题。


工作无失败




本质上,主线程是一个由进程组成的队列。 使用第三方线程时,可以在Objective-C中编写命令performSelectorOnMainThread:withObject:waitUntilDone: 多亏了她,任务才被放在主线程的队列末尾。 因此,如果主线程正忙于处理内存不足的通知,则调用此命令将允许您等待所有通知都已处理完毕,然后才开始加载和放置数据的复杂过程。 在Swift中,这看起来更简单。 DispatchQueue.main.sync释放主线程上的空间。


这是另一个例子。 我们释放了内存并解码了第三方流上的图像。 在视觉上滚动磁带变得更好。 由于我们正在测试iPod 5g,我们仍在丢失帧。 这是仍然支持iOS 10和11版本的最糟糕的测试模型之一。




如果遇到这种丢帧现象,您仍然可以查看录像带。 但是,仍然存在继续造成人员流失的过程。 还有其他方法可以使应用程序运行更快。


当然,优化应用程序并不总是那么容易。 但是,如果您有需要花费较长时间才能完成的任务,则应将它们放在后台线程中。 确保这些任务与UI不相关,因为许多UIKit类都不是线程安全的,也就是说,您不能在后端创建它们。


如果您需要处理第三方流上的图像,请使用Core Graphics。 不要隐藏系统库的显示。 记住内存不足警告。


欢迎来到MBLT DEV 2018


快来参加9月28日在莫斯科举行的第五届国际MBLT DEV 2018移动开发者大会 。 该网站上已有第一批发言人,最新的早鸟仍在发售中。 门票价格将于6月29日上涨。 现在以最低价格购买门票



在我们将在6月28日发布的文章的第二部分中,了解有关iOS中用户界面的实现,贝塞尔曲线的使用以及其他有用工具的信息。

Source: https://habr.com/ru/post/zh-CN415155/


All Articles