本文根据 Session 10181 整理。
作者:wiilen,现在上任于字节跳动西瓜视频,负责品质优化相关作业。
审核:Martin,现在上任于字节跳动 TikTok 团队,负责稳定性相关作业。
本文根据 Session 10181 整理。
功能优化听起来是一个困难重重的使命,需求追溯诸多目标,运用许多东西。这份功能优化攻略能协助你了解东西、目标、代码典范,进而让开发进程更加流畅,并为用户供给更优异的体会。
这份攻略比较于其他功能优化的 Session,更像是一本手册,指导你在遇到不同的功能问题时,应该运用哪份东西。而想要深化了解的话,则需求前往各个 Session。本文中也会参加作者在日常进程中运用这些东西的心得,协助咱们更好的了解苹果近年来在功能优化方向上为开发者供给的东西。
首要,该 Session 会对功能调试东西做一个全体介绍,包含五个首要的东西:Xcode Organizer、MetricKit、Instruments、XCTest、APP Store Connect API。之后会介绍一些各个领域常见的问题,如滑动功能、电量、网络运用状况等,然后供给解决和预防的计划。最后会介绍一些进一步的优化计划。
简介
功能优化就像一条长河,有许多半途的停靠站,需求不同的东西来指引方向;在每一个站点,也会有一些新东西需求学习。
现在有八个要害的目标用于衡量 App 的功能:电量运用状况、发动时刻、卡顿份额、内存运用状况、磁盘读写、滑动流畅性、运用中止、MXSignposts,这些都有东西能够追寻。
Session 中举了个例子,比照功能优化前后的体会,最直观的就是滑动的卡顿、掉帧。相同,功能优化的目标也是削减卡顿,让滑动更加流畅。每个功能目标都有对应的优化办法和常用东西。
电量运用状况
App 的电量运用状况会直接反应在「设置」-「电池」页面下,包含它在前台和后台的耗电量。优化电量运用状况的含义在于,用户不需求充电就能更久的运用手机。优化电量时,首要重视三个方向:CPU、网络、定位,之后也能够重视 GPU、音频播映以及蓝牙。
在一些东西的协助下,无论是在开发时,或者 App 版别发布后,咱们都能够追寻和确诊电量问题。在 Xcode 中运转 App 时,开发东西中也为咱们供给了能耗相关的面板,协助咱们调查当时的 App 的能耗运用状况。其间首要重视的是「高 CPU 运用(CPU High Utilization)」和「CPU 唤醒(CPU Wake Overhead)」。「高 CPU 运用」指的是 CPU 运用超越 20% 的状况,「CPU 唤醒」指的是 CPU 从闲暇状况被唤醒履行使命,这两者都会导致能耗的添加。CPU 峰值常出现在 UI 制作、处理网络数据、进行核算时,但当这些使命完成,App 在等候下一轮的用户操作时,CPU 的用量应该降到 0。
在这个面板中,也供给了进行 Time Profile 的按钮,点击今后能在 Instruments 中 App 的各项目标进行更细致的检测,包含 CPU 运用状况及记载下的调用栈。除此之外,也能够运用 Location Energy Model 来检测 Core Location
对能耗的影响,并防止在非必要时运用定位功能。
有时候咱们或许在 beta 版或现已发布的 App 上发现一些 bug,难以在本地重现,需求更多的日志和上下文来排查。此刻能够运用 MetricKit
,这是苹果从 19 年开端供给的一站式的功能监测结构,能搜集用户在运用 App 时出现的各种功能问题。要运用 MetricKit
,需求实现一个自定义的 AppMetrics
类,并恪守 MXMetricManagerSubscriber
协议。
class AppMetrics: MXMetricManagerSubscriber {
init() {
// 初始化时让 MXMetricManager 引证当时类的实例
let shared = MXMetricManager.shared
shared.add(self)
}
deinit {
// 析构时让 MXMetricManager 移除对本身的引证
let shared = MXMetricManager.shared
shared.remove(self)
}
// 能够聚合相同类型的数据,比如能耗的打点和 CPU 的运用数据
// Receive daily metrics
func didReceive(_ payloads: [MXMetricPayload]) {
// Process metrics
}
// Receive diagnostics
func didReceive(_ payloads: [MXDiagnosticPayload]) {
// Process metrics
}
}
最佳实践在上述的代码中,在初始化时让 MXMetricManager
引证当时类,然后在析构时免除引证,在 didReceive:
办法中聚合相同类型的数据。MetricKit
会供给上下文数据,以助于排查问题。当用户运用 App 时,苹果会从授权用户中搜集功能数据,然后在服务器中进行处理,你能够从 Xcode Organizer 等渠道获得这些数据。
苹果在 20 年时对 MetricKit 做了一次升级,相关 Session「What’s new in MetricKit」中对
MetricKit
做了一些相对完好的阐明,有爱好的读者能够阅览。也能够看看 20 年内参的翻译 「WWDC20 10081 – MetricKit 中的新功能」。简略来说,每 24 个小时,
MetricKit
会将功能数据搜集,然后打包上传到服务器,咱们能拿到可读性更高的数据。这些功能数据被包在MXDiagnosticPayload
中,包含电池相关目标(CPU、网络状况、GPU、定位)、功能目标(运用退出原因、发动耗时、内存运用状况)、用户交互相关目标(Hitch、卡顿状况)、磁盘写入目标、自定义目标(MXSignpost
打点的目标)。
从 Xcode 的 「Window」—「Organizer」中,能够点击「Battery Usage」来检查不同版别 App 的数据,这些数据被分类,展现在右侧的图表中。
假如最新版别的 App 出现了严峻的功能劣化,Xcode 13 中新供给的 Regressions 面板中会直观的展现。该面板中展现了一切最新版别中显著劣化的功能目标,一望而知。上图中 Reports 下 Crashes、Disk Writes、Energy 三个分类也能够协助咱们更准确的定位问题。这些数据也能够经过 App Store Connect API 访问,这样就能够本地剖析返回的 JSON 格式的数据了。
现在 Xcode 12 中也是有 Reports 及下面的三个分类的,但是没有集成
MetricKit
的话,只能看到 Crashes 中是有数据的。
假如要更深化的了解怎么改进耗电量,优化功能问题,能够观看 19 年的 Session 「Improving Battery Life and Performance」。本年的新 Session 「Analyze HTTP Traffic in Instruments」介绍了怎么剖析 HTTP 流量,也是一个有效的东西。
剖析 HTTP 流量之前确实是一个痛点,只能经过 Charles 和 Instrument 中的简略东西 「Network Activity Log」和「Network Connections」看到流量的耗费状况,这次新的东西供给了更具体的目标,具体咱们能够看 Session,下面是界面图,能够看出现在能按恳求发出时刻、恳求地址等来进行分类了,比以前只能从 Charles 抓包看会方便不少。
卡顿和滑动流畅性
这儿的卡顿原文中用词是 Hang,代表的是超越 250ms App 不呼运用户操作。超越 250ms 这个范围有些大,从上下文的联络中(指滑动流畅性)看,会更倾向所以和用户交互相关的描绘,所以这儿运用了「卡顿」这个翻译。
下一步是关于卡顿和滑动流畅性的,这两项目标表明 App 是否还呼应操作。卡顿指的是 App 不呼运用户的操作至少 250ms。App 长期的卡顿会导致用户强杀 App,对用户体会损害极大,需求优先处理。假如新一帧的内容在下一次改写时还没准备好,就会发生滑动卡顿。卡顿也会让用户用的难受,从而削减运用时刻,从卡顿开端优化功能,性价比较高。
还记得咱们之前展现过的流畅滑动吗?寻求这样的体会也是对用户最有协助的。在 Xcode Organizer 中,咱们能够跟踪卡顿和滑动流畅性的相关目标。假如注意到目标在继续上涨,或者像在滑动 Hitches 的页面中看到目标变黄或变红,那就需求更重视这方面的优化了。
苹果在 20 年的 Session 中提出了 Hitch 的概念,用以衡量滑动时的卡顿状况。Hitch 指的是 卡顿时刻(一帧拖延出现的时刻,ms)/ 总时刻(一般是 1 秒),低于 5 ms/s 阐明比较优异,高于 10 ms/s 阐明发生了较严峻的卡顿。
如上图所示,这些红色的柱形表明了更差的滑动体会,需求立刻着手优化。假如需求本地检查哪里出了问题,能够用 Instruments 中的 Thread State 或者 System Call Traces 东西来检查相关 trace。Thread State 能够展现当操作体系调度线程运转时,线程状况的时刻线,从中能看到具体某个当地线程被 block 了多久。System Call Trace 具体展现了有哪些体系办法调用,以及相应的履行时长。
Hitch 的具体介绍能够参阅「Find and fix hitches in the commit phase」和「Demystify and eliminate hitches in the render phase」。Render Loop 分为的 5 个阶段,Hitch 在 Instruments 中的数据也是按这些阶段展现的。
下图是本地跑的一个 Hitch 的截图,能够看到是 Commit 阶段导致了 Hitch。同时经过检查 Time Profiler,能够看到此刻在履行的相关办法,能够考虑经过离散或优化这些办法的耗时来下降一次 Commit 的耗时。
常见的除了 Commit 阶段会导致 Hitch,Render 阶段也或许会。Render 阶段导致 Hitch 的原因就比较常见了,如图层混合、阴影等或许用到离屏渲染的当地。
为了确保发布 App 时,没有带着会影响滑动体会的 bug,能够运用 XCTest 运转自动化 UI 测验,发动并滑动页面。在该项测验中,咱们指定想要丈量 scrollDecelerationMetric
这个目标,在 measure
办法的 block 中,用适宜的加速度来履行上滑操作。由于 measure
办法的 block 默认履行五次,在每次履行结束后,运用 XCTMeasureOptions
重置运用状况。这样就能履行测验代码开端丈量滑动功能,然后中止丈量、重置状况。
有时候想要重现呼应性相关的问题并不简略,得益于 MetircKit
,将它接入 App 后,咱们能够在这些问题发生时搜集测验和确诊成果。假如发生了卡顿的状况,在 iOS 14 中,MetricKit
会在 24 小时内搜集这些确诊信息。而在 iOS 15 和 macOS 12 中,能在功能问题出现时,马上获得确诊信息。结合这些实时的确诊信息以及测验技能,开发者就能快速定位并解决最严峻的呼应性问题。而对于滑动卡顿问题,iOS 15 在 MetricKit
中引入了新的 API,用 MXSignpost
来符号自定义动画。MXSignpost
是 MetricKit
中封装的 API,用于符号重要代码,以供长途搜集数据。
运用 mxSignpostAnimationIntervalBegin
API,能够符号自定义动画的起始方位。运用 mxSignpost
end API,能够符号动画的结束,并搜集这段时刻内发生 Hitch 的份额。这两个函数不只会搜集一些细粒度上的功能数据,也会捕获在这段时刻内发生的 Hitch。引荐阅览「Understand and Eliminate Hangs from your App」(《【WWDC21 10258】了解和消除 App 中的卡死》)这个 Session。假如想要深化了解怎么定位滑动卡顿问题,引荐阅览 「Eliminate animation hitches with XCTest」(《WWDC20 10077 – 运用 XCTest 消除动画卡顿
》)和「Explore UI Animation Hitches and the Render Loop」两个 2020 年的 Tech Talk。
磁盘写入
咱们现已走完了半程,接下来评论的是磁盘写入问题。磁盘写入操作会损耗用户的闪存,影响设备的运用寿命。频频的写入耗时严峻,会导致糟糕的用户体会及较差的功能,最好能分批进行写入操作。
在 App 上线前,能够用 Instruments 中的 File Activity 模版剖析是否有相关问题。这个模版以体系调用的形式记载文件体系的运用状况,因此能够轻松定位到哪些代码访问了文件体系。有许多办法约束磁盘写入,常见的比如批量处理写入操作、运用 CoreData 来存储频频更改的数据、防止快速创建和删除文件。
在剖析 App 时,也能够运用 XCTest 来写一些功能测验,以确保代码中没有过度的磁盘写入操作。运用 MetricKit
中的 measure
办法,传入 XCTStorageMetric
的实例,之后调用磁盘写入的相关代码。这能丈量写入磁盘的数据量,并在 Xcode 中显现成果。在测验中设定写的阈值后,假如测验代码的写入超限了,就无法经过该项测验。
假如现已发布了一个磁盘写入量较高的 App 版别,能够运用 Organizer 来追寻该版别在用户设备上的功能。磁盘写入目标展现了现版别的写入量和之前版别的比照趋势图。图中的峰值代表着 App 中现已有 bug,会发生许多的写入操作。此刻需求找到导致这个 bug 的原因,测验了解它是怎么发生的,并终究削减写入操作。
也能够在 Organizer 中的 Reports 下的 Disk Writes 中看到代码详情,其间展现了在 24 小时内写入超越 1 GB 的代码片段。调用栈展现了代码中哪里进行了过量的写操作,而在 Xcode 13 中,右侧新增了一栏 Insights,简述了能够怎么进行优化,削减磁盘写入。这些数据也会经过 App Store Connect API 供给。也能够经过 MetricKit
实时搜集这些数据。假如运用了 MetricKit
来监控 App 的磁盘运用状况,就能够运用 MXSignpost
记载要害的磁盘写入的途径,获取更准确的数据,从而找到优化空间。关于怎么定位并修正磁盘写入的问题,能够观看本年的 「Diagnose Power and Performance Regressions in your App」。
发动时刻与运用中止(Termination)
下一步,咱们会谈论发动时刻与运用中止。发动时刻指的是从用户点击 App 到首帧渲染结束的时刻。假如发动时刻过久,用户会觉得等的心累,而超长的发动时刻也会导致 App 被强行中止。假如体系结束了你的 App,用户需求从头走一遍发动流程,这比从后台康复要花更久时刻。
进程退出或许有多个原因,比如运用了超越体系约束的内存,或者发动耗时太长。App 被中止后,用户需求从头走一遍发动流程,而假如你的 App 不供给状况康复的能力,那用户需求手动操作来康复到之前到作业状况,更觉得心累了。
要是发现某一个版别的发动时刻大幅上升,能够看看 Organizer 中的 Launch Time 和新加的 Termination 面板,这能够将当时的 App 发动时刻和曩昔 16 个版别的进行比照,这样就能对 App 该有的发动时刻有个认知,也能够去 Termination 面板中检查 App 被体系中止,有多少次时由于发动耗时太久。就像上图中,有 70% 是由于发动耗时太久导致 App 被中止的。
确认了这个问题的存在之后,就能够经过本地的一些办法来找到具体是哪些代码导致的。首要,能够运用 Instruments 中的 App Launch 模版。该模版会运转 App 5 秒,搜集发动时办法耗时数据以及 Thread State Trace,从中能够获得线程被 block 的原因并修正它。其次,能够运用 XCTest 来获取发动时刻,经过 XCTApplicationsLaunchMetric
,在 measure
办法中进行丈量。假如想要自己进行更具体的剖析,也能够运用 MetricKit
来搜集每日的 App 中止数据。想了解怎么在 App 被中止时进行状况康复,能够参阅 2020 年的 「Why is my App Getting Killed」(《WWDC20 10078 – 为什么我的 app 被中止了?》)。
这一节的要点其实是发动时刻,这也是咱们平常在作业中重视的要点。运用的中止是发动时刻过长的恶果。发动时刻直接关系到用户需求多久翻开 App 并开端消费内容,能够说是争分夺秒的优化方位。
关于怎么进行优化,上文中没有具体介绍,其实也和各 App 的发动场景有关。比如有些 App 一翻开就会开端播映视频,有些一翻开是文章列表,需求加载的模块就不一样。但整体思路还是相似的:移除首帧显现前不必要的使命。这个使命影响用户消费首帧的内容吗?不影响就往后挪,放到闲时履行。
发动优化的难点有时候在于发现问题,看 Instruments 中跑出的成果,有时候会有无从下手的感觉,或许是由于:
- 不知道这个办法是不是必要的;
- 不知道这个办法有没有优化空间。
个人经历上,第一点一般能够分为两个过程解决:存量办法治理、新增办法管控。假如刚开端做优化,对现有的办法或许不太熟悉,这需求长期的整理,列出表今后,对相关办法进行标注,拖延不必要的办法。而新增办法管控则是对每个版别中新增的,影响发动的办法做总结,每次改动到发动相关的代码时严厉 review,了解影响范围,确保是有必要的。
第二点上,能够从办法的绝对耗时开端,从高到低整理,出资回报率比较高。耗时严峻的办法一般也有相对较大的优化空间,能够结合 Trace 看看。
这儿也引荐一个东西:AppleTrace。它能用发动火焰图的办法来剖析办法耗时,供给了和 App Launch 模版不一样的思路。
内存
最后,咱们会评论内存相关的问题。内存是 App、操作体系和内核间共享的资源,假如 App 运用了超出约束的内存,就会被体系给中止运转,然后下次用户从头发动,又要从头走一遍发动流程。假如在 App 中参加了图片读取或上传的相关功能,就要当心是否或许导致内存运用超限,多重视 Organizer 中的 Memory 和 Terminations 面板。
假如发现内存运用在新版别中达到了峰值,能够运用 Instruments 中的 Leaks、Allocations 和 VM Tracker 模版来进行剖析。Leaks 会记载进程的堆,并检查泄露的内存。Allocations 会剖析 App 中内存的生命周期。VM Tracker 会随时刻展现 App 的虚拟内存空间。相同,能够运用 MetricKit 来获取这些信息并进行更具体的剖析。
// Collect memory telemetry
func saveAppAssets() {
mxSignpost(OSSignpostType.begin,
log: MXMetricManager.makeLogHandle(category: "memory_telemetry"),
name: "custom_memory")
// save app metadata
mxSignpost(OSSignpostType.end,
log: MXMetricManager.makeLogHandle(category: "memory_telemetry"),
name: "custom_memory")
}
和运用 MetricKit
搜集内存和运用中止信息相同,能够经过在要害代码前后加上 MXSignposts
打点,来获取内存运用的要害信息。
想要了解怎么检测和了解怎么解决内存问题,能够看看本年的「Detect and Diagnose Memory Issues」。
内存相关的东西或许是比其他方面的东西要全面的多,这也是由于内存优化一向是功能优化的要点。除了上面介绍的 3 个 Instruments 中的东西,Xcode 中还供给 Memory Graph 来协助查找循环引证,Product 中的 Analyze 也能够对或许的循环运用进行剖析。想要深化了解内存问题的话,18 年的 Session「iOS Memory Deep Dive」是一个不错的参阅。
总结
咱们来总结今天所讲的内容。功能优化对开发者们来说是一件很有挑战性的事情。在曩昔几年里,苹果供给的东西协助许多开发者优化了功能。长期以来,Snapchat 一向致力于改进其运用程序的发动体会,并推进下降中止率。去年,Snapchat 削减了 99% 预期之外的运用中止状况。运用该 Session 中介绍的东西,开发者们也能够做到这一点。
假如你对功能优化东西不熟悉,引荐看看 2020 年的「Diagnose Performance Issues with the Xcode Organizer」(《WWDC20 10076 运用 Xcode Organizer 确诊功能问题》)、「What’s New in MetricKit」(《WWDC20 10081 – MetricKit 中的新功能》)、「Identify Trends with the Power and Performance API」(《WWDC20 10057 – Identify trends with the Power and Performance API》)三个视频,以及 2019 年的「Getting Started with Instruments」。深化了解了这些目标与东西之后,期望咱们能够在这些东西的协助下,供给功能最优的 App 给用户。作为操练,咱们能够去 Xcode 的 Organizer 中看看 App 的功能趋势,运用 Instruments 中不同的模版来检测功能,写写 XCTest 来提早检测问题,用 MetricKit 来强化数据搜集与剖析。
本次的 Session 内容就到此为止了,期望咱们能在读完后花一些时刻开端测验优化功能,多多加油。
重视咱们
咱们是「老司机技能周报」,一个继续寻求精品 iOS 内容的技能大众号。欢迎重视。
重视有礼,重视【老司机技能周报】,回复「2021」,收取 2017/2018/2019/2020 内参
支持作者
在这儿给咱们引荐一下 《WWDC21 内参》 这个专栏,一共有 102 篇关于 WWDC21 的内容,本文的内容也来源于此。假如对其余内容感爱好,欢迎戳链接阅览更多 ~
WWDC 内参 系列是由老司机牵头安排的精品原创内容系列。 现已做了几年了,口碑一向不错。 首要是针对每年的 WWDC 的内容,做一次精选,并号召一群一线互联网的 iOS 开发者,结合自己的实际开发经历、苹果文档和视频内容做二次创造。