性能iOSiOS点点滴滴

高性能iOS应用开发 - iOS性能

2020-05-08  本文已影响0人  Q以梦为马

《高性能iOS应用开发》是一本质量很高的 iOS 书籍,我从此书中系统的学到了很多东西。这篇博客是《高性能iOS应用开发》一书第三部分“iOS性能”的读书笔记,因为我对 APP 性能比较感兴趣,所以就先从第三部分“iOS性能”开始了。

1. 应用启动

iOS 应用在启动时会调用 UIApplicationMain 方法,并传入 UIApplicationDelegate 类的引用。委托接收应用范围的事件,并且有明确的生命周期,application:didFinishLaunchingWithOptions: 方法表明应用已经启动。

应用的窗口有一个 rootViewController,对应的 UIViewController 对象同样具有明确的生命周期。UIViewController 的方法viewDidAppear:执行时,说明启动已完成。

APP 启动过程中,应尽量减少不必要的操作,从而缩短应用的启动时长,实现更好的用户体验。应用有四种启动类型。

1.1 首次启动

安装应用后的首次启动。此时没有之前的状态,也没有本地缓存。这意味着将会出现以下两种情况中的一种:没有需要加载的内容(因此加载时间会缩短),或者需要从服务器上下载初始数据(可能需要很长的加载时间)。在应用首次启动时,你可以选择提供引导图来总结应用的功能和用法。

首次启动时,应用通常会执行多个任务:

上述列举的内容只是应用在首次启动时可能执行的任务。其中一些还会在后续启动中执行。问题是,任务数量的快速增加必然会导致应用的启动速度变慢。

那么怎么避免这样的问题呢?可以遵循下述具体步骤,拆解任务列表,从而获得更高的性能。

1.2 冷启动

应用后续的启动。在启动期间,可能需要恢复原来的状态,例如,游戏中达到的最高等级、消息应用中的聊天记录、新闻应用中上一次同步的文章、已登录用户的证书,或者 仅仅是用户已经使用过的引导图标记符。

冷启动中一个较为重要的任务是,载入之前的状态。在应用中,显示给用户(登录后)的第一个画面是 feed 流。如果用户在以前的启动中登录过,并且数据已经同步,那我们就会考虑加载之前已经缓存的 feed 流。

为了实现向用户展示 feed 流的任务,必须向服务器请求最近的更新,同时还要从本地缓存加载数据。这些行为是不用思考就知道的。但是,以下几点却是不容忽视的。

1.3 热(重)启动

这是指当应用处于后台,但并未被挂起或关闭时,用户切换至应用而触发的启动。在这种情况下,当用户通过点击应用图标或深层链接返回应用时,不会触发启动时的回调,而是直接用 applicationDidBecomeActive:(或 application:openURL:source:annotation:)回调。

通常来说,这种情况和继续执行没什么区别,只是视图控制器可能需要处理一些额外的事件。

热启动是指切换到一个已经运行了的应用。两个原因可能会使应用变成非激活状态:一是用户向下拉拽状态栏,二是用户点击 home 键或切换至其他应用。

热启动有两种情境:

1.3.1 应用重启

当用户点击应用图标时,一般不需要执行其他特殊的操作。

应用处于安全状态,或者运行很多动画时,可以监测背景和前景通知。在第一种情况下, 应用每次进入前景状态时,都会展示登录界面;在后一种情况下,动画或者游戏状态会被暂停,需要恢复。

1.3.2 深层链接

当应用接收到 application:openURL:sourceApplication:annotation: 回调时,期望能跳转
到应用的特定页面,实现用户想要完成的操作。但此时的目标应用可能已经发生变化,处于某一特定状态了。

如果深层链接需要从服务器获取数据,那么可以先展示与深层链接相关的原始页面,或者先展示一个进度条,等从服务器获取到了最新数据,再执行刷新操作。

1.4 升级后的启动

应用升级以后的启动。通常而言,升级后的启动与冷启动没有差别。但是,不同的启动叫法表明了本地存储发生变化的时刻是不同的,这些变化包括模式、内容、之前版本挂起的同步操作,以及内部的 API/ 默认依赖。

应用升级后的首次启动将遵循下列情形之一:

如果无本地缓存或应用决定放弃缓存(例如,数据不可用或从服务器同步获取更快),则不需要进行特殊处理。本地数据发生改变时通知用户。以下的最佳实践可以让用户有更好的体验。

2. 用户界面

当与 UI 进行交互时,大部分用户才注意到性能问题。如果某个应用在数据同步和刷新上耗时较长,或用户交互不够稳定,那么应用会被认为是迟钝的。

功耗、网络使用率、本地存储等因素对用户来说是不可见的。因此,虽然这些因素是解决性能问题的要素,但 UI 却是应用的门面,如果 UI 反应迟钝,则必然会直接影响用户的反馈。

还有一些无法控制的外部因素,如下。

2.1 视图控制器

在应用开发的最初阶段,视图控制器都较为精简,状态较好。随着时间的推移,这些视图控制器慢慢变成了所有业务逻辑的垃圾场,代码量也增长至几千行。虽然逻辑的“总量”是不可避免的,但将代码重构成短小、可复用的方法是很好的主意。这样不仅能解除耦合,还可以发现无用的、重复的代码。

下面列举了创建视图控制器时需要遵循的一些较为基本的最佳实践。

2.1.1 视图加载

视图初始化时会涉及两个方法——loadView 和 viewDidLoad。

如果通过覆写 loadView 方法创建了自定义 UI,你需要牢记以下几点。

在执行过程中,应该尽量缩短在 viewDidLoad 方法上花费的时间。具体来讲,将要被渲染的数据应该是已经可用的,或是在其他线程进行加载的。在 viewDidLoad 的完成中发生的任何延迟,都将导致与视图控制器相关的 UI 展示发生延迟。用户会卡在应用启动或前一个视图控制器中。

2.1.2 视图层级

展示出来的 UI 是由嵌套在树形结构中的各层次视图组成的,它们的位置受自动布局或其他编排方式的约束。视图结构和渲染包括以下步骤。

视图层次越复杂,构建和渲染视图消耗的时间也就越长,因此要尽量减少视图层级。

2.1.3 视图可见性

视图控制器提供了四个生命周期方法,以接收有关视图可视性的通知。

以下列举了一些高效使用生命周期事件的最佳实践。

2.2 视图

优化视图方面最具挑战性的部分是,很少有普适于所有视图的技术。每个视图都有其独特
的用途,且大部分的优化技术都与特定的视图和暴露出的 API 有关。

2.2.1 UILabel

这可能是 iOS 上最常用的视图了。它虽然看起来简单,但是渲染代价却不容小觑。下列是涉及的一些复杂步骤。

具体说明每个 UILabel 是一件工作量很大的事情。使用较少的标签,更容易管理效果,使用较多的标签,你就需要多留意这些标签的创建、配置和重用。

2.2.2 UIButton

渲染按钮的方式有以下四种:

2.2.3 UIImageView

在渲染代价较大的各种 UI 元素中,图像首屈一指。在使用 UIImage 和 UIImageView 时,遵循以下的最佳实践可以提升性能。

2.2.4 UITableView

无论是在新闻应用、邮件应用、照片流,还是其他的应用中,UITableView 都是最常用于显示数据的视图。UITableView 提供了一个展示信息条的极好选择,这些信息条既可以是同一类别,也可以是不同类别。

UITableView 绑定了两个协议。

下列是使用 UITableView 时需要牢记的一些最佳实践。

2.2.5 UIWebView

UIWebView 是用于渲染未知或动态内容的最常见视图。

虽然有些应用可能全部都是原生的,但还是有需要使用 UIWebView 的场景,以下是一些常见场景。

使用 UIWebView 时,请将以下几个最佳实践牢记在心。(需要注意的是,关于 UIWebView 能做的事情非常少,并非都是关注性能的;相反,此处的重点是以最恰当的方式展示 HTML 内容。)

2.3 自动布局

通过 Auto Layout,可以描述一个元素距另一元素的距离(水平或垂直)、其大小(宽度或高度),或其与另一元素的对齐方式(水平或垂直)。

关于 Auto Layout 的性能问题,书中只介绍了 Auto Layout 在视图数量很多时,消耗会比 Frame 布局大很多,介绍的比较笼统,具体还可以参考下这篇博客:从 Auto Layout 的布局算法谈性能

3. 网络

在应用中使用网络是必不可少的,但减少网络延迟的方法却是有限的,因此,你应该着手对网络条件进行最大程度的优化,并预先对不同的场景进行规划。

3.1 指标和测量

在网络中完成的大多数工作是无法控制的,因此确定衡量的标准非常重要。接下来会列出在性能优化相关的测量中更为重要的一些指标。

3.1.1 DNS查找时间

发起连接的第一步是 DNS 查找。如果你的应用严重依赖网络操作,DNS 的查找时间会使应用变慢。

为了最大限度地减少 DNS 查询时间所产生的延迟,你应该遵循以下的最佳实践。

3.1.2 SSL握手时间

为了安全起见,可以假设应用中所有的连接均是通过 TLS/SSL 的(使用 HTTPS)。HTTPS 在连接开始时,先进行 SSL 握手,SSL 握手主要是验证服务器证书,同时共享用于通信的随机密钥。这一操作听起来简单,但是却有很多步骤,还会耗费较多时间。

你应该遵循以下的最佳实践。

3.1.3 网络类型

一般情况下,iPhone 和 iPad 可以使用 WiFi、4G、3G 等网络连接到互联网。

遵循以下的最佳实践。

3.1.4 延迟

延迟是指从服务器请求资源时,在网络传输上花费的额外时间。设置用于测量网络延迟的系统是很重要的。

网络延迟可以通过使用请求过程中花费的总时间减去服务器上花费的时间(计算和服务响 应)来测量:

Round-Trip Time = (Timestamp of Response - Timestamp of Request)
Network Latency = Round-Trip Time - Time Spent on Server

花费在服务器上的时间可以由服务器来计算。对客户端而言,往返的时间是准确可用的。服务器可以将花费的时间放在响应的自定义头部,然后客户端就可以用来计算延迟了。

如果你有数据来分析任何模式下的延迟,还需跟踪下列数据。

3.2 应用部署

随着对这些指标的统计,你可以更好地规划应用的部署。这不仅包括服务器、服务器的位置和容量,还包括客户端,以及如何在给定的场景下获得最好的。

3.2.1 服务器

在查看网络延迟的地域分布时,我们可以使用这个信息为数据中心选择适当的位置。如果使用托管的数据中心提供商,不妨选择有多个地理位置的,如 Amazon AWS 或 Rackspace Cloud。如果你有自己的数据中心,那么应该确保它们在地理上是分散的。

无需多想,服务器应该安装在多个位置,这样你可以更好地服务本地内容。

以下是一些应该遵循的最佳实践。

3.2.2 请求

为了恰当地设置网络,正确地配置 HTTP/S 请求很重要。你应该遵循以下的最佳实践。

3.2.3 数据格式

选择正确的数据格式和选择网络参数一样重要。一些选择可能会使应用的性能产生很大的不同,比如对无损图像压缩使用 PNG 还是 WEBP。

如果你的应用是以数据为导向的,那么选择适合其传输的正确格式很关键。其他协议支持的功能也可以提供帮助。

在选择数据格式时,你应该遵循以下的最佳实践。

3.3 工具

Charles 是一个非常强大的网络调试代理。使用比较简单,这里不做过多介绍,如有需要可参考网上博客。

推荐一些之前收集的性能优化的博客:

4. 数据共享

有时你会需要与其他应用共享数据,或访问设备上其他应用的共享数据。共享数据的场景包括以下几个。

4.1 深层链接

在移动应用的上下文中,深层链接包括使用统一的资源标识符(uniform resourceidentifier,
URI),其链接到移动应用内的特定位置,而不是简单地启动应用。

深层链接为应用之间的共享数据提供了解耦的方案。与访问网站时的 HTTP 网址类似,iOS 中的深层链接通过所谓的自定义 URL scheme 来提供。你可以配置自己的应用,让它响应唯一的 scheme,操作系统会确保无论何时使用该 scheme,都由你的应用进行处理。应用可以响应任何数量的 scheme。

不论是访问共享数据,还是对外共享数据,深层链接可能是最常用的选项,同时,优化创建和解析的时间也很重要。以下列表涵盖了可以遵循的一些最佳实践,从而让应用实现最优性能。

4.2 剪贴板

官方文档对剪贴板的描述如下。

剪贴板是用于在应用之内或之间交换数据的安全且标准化的机制。许多操作取决 于剪贴板,特别是复制—剪切—粘贴。......但你也可以在其他情况下使用剪贴板,例如,在应用之间共享数据时。

可通过 UIPasteBoard 类使用剪贴板,该类可以访问共享存储库,写对象和读对象在共享存储库中进行数据交换。写对象也被称为剪贴板所有者,将数据存储在剪贴板实例上。读对象访问剪贴板,将数据复制到其地址空间中。

与深层链接相比,剪贴板具有以下优点。

使用剪贴板时,你应该遵循以下的最佳实践。

5. 安全

应用可能会在未知的执行环境中运行,并通过未知的传输网络交换数据,因此,应始终将安全性作为首要任务之一,以便保护用户及应用的敏感数据。

不论是通过代码的执行(例如,从 1024 位 DSA 密钥的加密密钥转为 2048 位 RSA 的加密密钥)还是通过用户干预(例如,引入双因素认证或应用 PIN),任何附加的安全层都会导致应用变慢。因此,在保证用户完成意图的前提下,你需要对添加的安全措施(会导致延迟)进行权衡。

5.1 应用访问

5.1.1 匿名访问

应用可能需要验证,也可能不需要验证。

有两个选项可用于识别设备:供应商的标识符(Identifier for Vendor,IDFV)和广告商的 标识符(Identifier for Advertiser,IDFA)。

IDFV 是设备上每个应用的持久唯一的标识符,用于向应用的供应商标识设备。应用包 ID 的一部分用于生成 IDFV,因此,即使应用来自同一家公司, IDFV 也可能不同。

IDFA 是可重置的标识符,在设备上的所有应用中是唯一的。正因为在众多应用中是唯一的,所以它才是真正唯一的 ID。但是,IDFA 可以被用户重置。此外,苹果公司对它的使用设置了限制,你必须保证在提交应用到 iTunes Connect 审核时使用它。此 ID 只应由广告投放系统使用。

5.1.2 认证访问

当需要识别用户时,你需要认证访问。这并不意味着认证必须在你的应用中完成。以下是一些可用的认证选项。

5.2 网络安全

前面已经对网络进行了深入的讨论。这里将讨论在与远程设备通信中与安全有关的最佳实践,该远程设备可以是服务器,也可以是点对点设备。

5.2.1 使用HTTPS

假设你将 HTTP 作为底层消息传递协议(TCP 是传输层协议),那么你必须通过 TLS/SSL 使用它。这也就是说,你应该一直使用 HTTPS。但是,使用 HTTPS 有几个问题。如果这些潜在风险未得到解决,则 HTTPS 可能会受到影响。

1.CRIME攻击

不要使用 SSL/TLS 压缩。如果你现在在使用,请在继续之前立即关闭它。这会让你处于较大的风险当中。使用 TLS 压缩(gzip、deflate 或其他格式),任何请求都会受到 CRIME(Compression Ratio Info-leak Made Easy,压缩率使信息很容易泄露)攻击。要想缓解风险,可以关闭 TLS 压缩,并给每个响应发送反 CRIME cookie,较为简单的方式是发送一个唯 一的随机序列 cookie。

2.BREACH攻击

如果使用请求/响应正文压缩(Transfer-Encoding = gzip或deflate),你的通信会受到BREACH(Browser Reconnaissance and Exfiltration via Adaptive Compression of Hypertext,通过自适应超文本压缩的浏览器侦听和渗透)攻击,这种攻击类型于 2012 年 9 月首次发现。当满足以下标准时,就会存在风险。

没有单一的方法可以降低这种风险。The Breach Attack网站按有效性列出了以下方法。

5.2.2 使用证书锁定

HTTPS 不是万灵药——采取 HTTPS 不会神奇地确保所有的通信都是安全的。HTTPS 的基础是对公钥的信任,该公钥用于加密初始消息(在 SSL 握手期间)。中间人(man-in-the- middle,MITM)攻击会捕获用于加密消息的密钥。

不让请求变成无效的唯一方法就是信任,该信任由网络库放置在接收到的证书之中。证书只是签名的公钥。因此,如果网络库信任签名者,那它也会信任主机提供的公钥。黑客提供的假的根证书成为了让所有安全措施崩溃的罪魁祸首。

这个问题的解决方案就是所谓的证书锁定。这种方案的工作原理是,通过只信任一个或几个能够作为应用根证书的证书,应用创建一个自定义的信任级别。这允许应用仅信任来自白名单的证书, 确保设备上永不安装那些允许网络监视的未知证书。

5.3 本地存储

与通过网络交换的数据类似,存储在设备上的数据是不能防止被篡改的,而且如果不小心处理的话,入侵者是可以读取或修改数据的。以下是需要注意的几个要点,以及为了保护本地存储空间需要遵循的最佳实践。

5.4 数据共享

共享数据和处理传入数据时遵循的简单基本规则是:不要信任对方。

当接收数据时,总是进行验证。应用对数据的唯一假设应该是,它可能是无效且错误的。为了提高安全性,要求数据进行签名。

同样,因为不知道哪个应用会处理数据,所以永远不要发送敏感数据。如果你确实需要共享敏感数据,那么提供令牌,然后要求其他应用从你的应用(或服务器)请求数据。

5.5 安全和应用性能

额外添加的加密或安全措施会计入总内存的消耗之中,同时还会增加处理时间。你没有办法在所有维度上进行优化,只能做一些权衡。

有时,并非必须使用 2048 位的 RSA 密钥,1024 位的 DSA 密钥也许就已经足够了。其他时候,Rijndael 这样的对称加密算法就足以保护数据的安全了。

从钥匙串检索初始值可能会导致加载时间延长。你在使用时应该小心谨慎。

证书锁定有其自己的成本,有可能会减慢所有的网络操作。

创建和验证数据签名需要计算内容哈希,这意味着会产生额外的内容传递。根据内容的大小,这可能需要较多时间,更不用说计算和验证数字签名所需的额外时间了。

所有这些步骤会快速叠加起来。也许你有了世界上最保险和最安全的应用,但如果仅加载程序就需要 30 分钟,估计也没有人想使用它。对于这一点,即使 5 秒钟都可能对用户体验产生负面影响,甚者永远失去用户,尤其在其他应用可以满足同样需求的情况下。


相关文章:高性能iOS应用开发 - 核心优化

上一篇下一篇

猜你喜欢

热点阅读