1. 简介
2. 性能指标
性能指标是面向用户的各种属性。每个指标可能是一个或多个可测量工程参数的一个要素。
2.1 内存
内存涉及运行应用所需的 RAM 最小值,以及应用消耗的内存平均值和峰值。最小内存值,会严重限制硬件,而更高的内存平均值和峰值意味着更多的后台应用会被强制关闭。
同时还要确保没有泄漏内存。随时间流逝而持续增长的内存消耗意味着,应用很可能会因 为内存不足的异常而崩溃。
2.2 电量消耗
在编写高性能代码时,电量消耗是一个需要重点处理的重要因素。就执行时间和 CPU 资 源的利用而言,我们不仅要实现高效的数据结构和算法,还需要考虑其他的因素。如果某 个应用是个电池黑洞,那么一定不会有人喜欢它。
电量消耗不仅仅与计算 CPU 周期有关,还包括高效地使用硬件。除了要实现电量消耗最 小化,还要确保不会影响用户体验。
2.3 初始化时间
应用在启动时应执行刚好够用的任务以完成初始化,从而满足用户的使用需求。执行这些 任务消耗的时间就是应用的初始化时间。刚好够用是一个开放式用语——正确的平衡点取 决于应用的需要。
在首次使用应用时创建对象并进行初始化是一个合理的选择,例如,直到需要使用对象时 才创建对象。这种方式被称为惰性初始化。这是一种很好的策略,但也要考虑不能让用户 总是在执行后续任务时等待。
下面列举了你可能想在应用初始化阶段执行的一些动作,排名不分先后。
检查应用是否为首次启动。
检查用户是否已经登录。
如果用户已经登录,尽可能地载入之前的状态。
连接服务器以拉取最新的变更。
检查应用是否由某个深层链接唤起。如果是,还需要载入深层链接相应的 UI 和状态。
检查是否存在应用上次启动时挂起的任务,需要时恢复它们。
初始化后续需要使用的对象和线程池。
初始化依赖项(如对象关系映射、崩溃报告系统和缓存)。
2.4 执行速度
一旦启动应用,用户总是希望它可以尽可能快地工作。一切必要的处理都应该在尽可能短 的时间内完成。
例如,在照片应用中,用户通常希望看到调整亮度或对比度等简单效果的实时预览效果。 因此,相应的处理需要在几毫秒内完成。
2.5 响应速度
每个应用都应该快速地响应用户交互。在应用中所做的一切优化和权衡最终都应该体现在响应速度上。
App Store 中有许多应用可以完成相似或相关的任务。这为用户提供了很大的选择空间,而 用户基本都会选择响应最快的应用。
2.6 缓存
针对任何在服务器上存储数据或通过外部来源刷新数据的应用,开发人员应该对本地存储的使用有所规划,以便应用具备离线浏览的能力。
例如,用户都希望邮件应用能够在无网络或设备离线的情况下浏览历史邮件。
同样,新闻应用也应该可以在离线模式下显示最近更新的新闻,并标记出每条新闻是否已读。
然而,从本地存储中载入和同步数据应该迅速、便捷。这不仅需要选择要在本地缓存的数 据和要优化的数据结构,还需要提供一系列的配置选项并确定数据同步的频率。
如果你的应用使用了本地存储,那么请提供一个清除数据的选项。遗憾的是,市场上的大 部分应用都没有提供此选项。更让人烦恼的是,一些应用竟然会消耗数百兆的存储空间。 用户会频繁地卸载这些应用来回收本地存储。这会导致糟糕的用户体验,从而威胁应用的 成功。
一定要向终端用户提供清空本地缓存的选项。如果用户开启了 iCloud 的备份功能,那么应用的数据将会消耗用户的存储限 额,请谨慎使用。
2.7 互操作性
用户可能会使用多个应用来完成某个任务,这就需要这些应用直接提供互操作的能力。
例如,一个相册可能需要一个幻灯片应用来实现最佳的浏览体验,但需要另一个应用来编辑 照片。其中浏览照片的应用要能够将照片发送到编辑器,并接收编辑后的图片。iOS 为实现应用间的互操作和数据共享提供了多种机制,其中包括 UIActivityViewController、 深层链接、MultipeerConnectivity 框架,等等。
为深层链接定义良好的 URL 结构与编写优异的代码来解析 URL 同样重要。类似地,使用 共享对话框共享数据时,精确识别用于分享的数据非常重要,同时,在处理不同数据源传 入的数据时还要注意安全隐患。
如果某个应用向附近设备共享数据时需要花费很长时间准备数据,那么用户体验就会非常 糟糕。
2.8 网络环境
移动设备会在不同网络环境下使用。为了确保能够提供最好的用户体验,你的应用应当适应各种网络条件:
高带宽稳定网络
低带宽稳定网络
高带宽不稳定网络
低带宽不稳定网络
无网络
为用户提供进度指示或错误信息是相对合理的方式,无尽的等待或崩溃则让人无法接受。
2.9 数据刷新
即使没有提供离线浏览能力,你仍然可以从服务器端周期性地刷新数据。刷新的频率和每 次传输的数据量将决定数据传输的总量。如果传输的字节数过大,那用户必然会快速耗尽 自己的流量计划。当流量消耗大到一定程度时,你的应用很可能会流失用户。
在 iOS 6.x 或更低版本中,在后台运行的应用不能刷新数据。从 iOS 7 开始,应用可以在后 台周期性地刷新数据。对于在线聊天类应用,持久的 HTTP 连接或原生 TCP 连接可能会非 常有用。
2.10 多用户支持
家庭成员间可能会共享移动设备,或者一个用户可能会拥有同一应用的多个账号。例如, 兄弟姐妹间可能会共享一个 iPad 来玩游戏。再比如,家庭成员可能会在旅游时配置一个设 备来查收全家人的电子邮件,以减少漫游费用,尤其是在境外旅游时。类似地,一个人也 可能会配置多个电子邮件账号。
是否支持多个并发用户取决于产品的需要。一旦决定提供此类功能,请参考以下准则。
添加新用户应尽可能高效。
在不同用户之间更新应尽可能高效。
在不同用户之间切换应尽可能高效。
用户数据的界限应该简洁且没有 bug。
2.11 单点登录
如果你已经创建了多个允许或需要登录的应用,那么支持单点登录(single sign-on,SSO) 是非常棒的选择。如果用户登录了一个应用,只需要点击一次,就可以登录到其他的应 用中。
这个过程不仅需要支持跨应用的数据共享,还需要分享状态、跨应用同步等。例如,如果 用户注销了其中某个应用,则通过 SSO 登录的所有其他应用也应能注销掉。
此外,应用之间的同步应该是安全的。
2.12 安全
安全对移动应用来说是最重要的,因为敏感信息可能会在应用间共享。因此,对所有通信以及本地数据和共享数据进行加密就显得尤为重要了。
实现安全需要更多的计算、内存和存储,但这与最大化运行速度、最小化内存和存储使用 的目标相冲突。
因此,你需要在安全和其他因素之间进行权衡。
引入多个安全层会影响性能,并对用户体验造成可感知的负面影响。如何设定安全的基线 需要参考对用户群体的统计分析。此外,硬件在其中扮演了重要的角色:选择会因为不同 设备的计算能力而有所不同。
2.13 崩溃
应用可能会而且确实会崩溃。过度优化会导致崩溃。同样,使用原始 C 代码也可能会导致 崩溃。
高性能的应用不仅应尽可能地避免崩溃,还应该在崩溃发生时优雅地恢复,尤其是在进行 某个操作的过程中发生崩溃时。
3. 性能分析
我们在前面讨论过一些参数,通过测量它们来分析应用的方式有两种:采样和埋点。
3.1 采样
顾名思义,采样(或基于探测点的性能分析)是指以一定的周期间隔采集状态,这通常需 要借助工具。由于不会干扰应用的执行,因此采样可以 很好地提供应用的全景图。采样的不足之处在于它不能返回 100% 精确的细节。如果采样 的频率是 10 毫秒,那么你就无法得知在探测点之间的 9.999 毫秒内发生了什么。
采样可以作为初始的性能调研手段,并可用于跟踪 CPU 和内存的使用情况。
3.2埋点
通过修改代码,记录细节信息的埋点能够提供比采样更加精确的结果。你既可以在关键部 分主动埋点,也可以在性能分析或处理用户反馈时有针对性地埋点,以便解决问题。
因为埋点需要注入额外代码,所以它一定会影响应用的性能,对内存或速度(或同时对二者)造成损害。
4. 测量
针对工程配置、安装和代码实现共有三类任务。
• 构建与发布 确保能够轻松地构建和发布应用。
• 可测试性 确保你的代码能够同时在模拟数据和真实数据之上工作,其中包括能够模拟真实场景的 隔离环境。
• 可跟踪性 确保你能够通过明确问题发生的位置和代码行为来处理错误。
4.1 设置崩溃报告
崩溃报告系统收集用于分析应用的调试日志,获取崩溃日志的方式有很多。
手机隐私设置
Xcode的Devices and Simulators
Xcode的Organizer获取线上奔溃日志
App Store Connects
Bugly
firebase
友盟
听云
KSCrash等开源组件
4.2 应用埋点
对应用进行埋点是了解用户行为的一个重要步骤,但更重要的目的是识别应用的关键路径。注入特定的代码以记录关键指标是提升应用性能的重要步骤。
埋点不应该取代日志。日志可以非常详细。但因为向服务器报告时会消耗网 络资源,所以你应该尽可能少地埋点。 因此,只对你和其他工程或产品团队的成员感兴趣的事件进行埋点是非常重 要的。(这些事件要包含足够多的数据以满足重要报告的需要。) 埋点和过度埋点之间并没有清晰的分界线。一开始应仅对少量报告进行埋 点,然后随着时间的推进逐步增加埋点的覆盖率。
4.3 日志
日志和埋点之间存在着细微的差别。埋点可以看作日志的子集。被埋点的任何数据都应该 记录在日志中。埋点承担了为聚合分析发布关键性能数据的职责,日志则提供了用于在不同级别跟踪应用 的细节信息,比如 debug、Verbose、info、warning 和 Error。日志的记录会贯穿应用的整 个生命周期,而埋点只应该用在开发的特定阶段。
埋点数据会发送到服务器,日志是记录在设备本地。
个人博客: 🏡 ForgetSou