iOS 提高应用性能小技巧

阅读原文数据库

在开发 iOS 应用程序时,确保应用程序具备良好的性能是相当重要的。然而,因为开发周期的局限性,让咱们很容易忘记决策对性能的影响。这篇文章整理了一些提高性能的小技巧,但愿会对你有所帮助。json

初级技巧

使用 ARC 来管理内存

ARC 自动引用计数,会自动为代码在合适的位置添加ratain/release,你没必要手动来管理它。这样,就消除了最多见的内存泄漏问题。api

除了帮助你避免内存泄漏以外,ARC 还能够经过确保对象在不在须要时当即释放来提高性能。数组

值得注意的是,ARC 并不能消除全部的内存泄漏。即便使用 ARC ,仍可能会出现内存泄漏,这主要是因为块、引用循环、对 CoreFoundation 对象管理不善或糟糕的代码引发的。缓存

在适当的地方使用重用标识

在适当的地方使用重用标识,如:UITableViewCells、UICollectionViewCells、甚至是UITableViewHeaderFooterViews。bash

若是不使用重用标识,表视图在每次显示行的时,会配置一个一个全新的单元格,这是一个昂贵的操做,会影响滚动性能。服务器

在iOS 6以后,你也须要为页眉和页脚视图,以及 UICollectionView 的单元格和补充视图使用重用标识。网络

尽量将视图设置为不透明

能够经过将 opaque 属性设置为 YES 来设置,这个属性默认是 YES。数据结构

该属性会向绘图系统提供有关如何处理视图的提示。若是设置为 YES , 绘图系统会将视图视为彻底不透明,从而容许绘图系统优化绘图操做,从而提高性能。app

可使用 Debug\Color Blended Layers 选项来查看哪些视图未设置为不透明。

避免使用臃肿的 XIBs

若是必需要使用 XIBs ,要尽量使它们简单。当将xib加载到内存时,它全部的内容都会加载到内存中,包括图像。若是你没有当即使用它,那么就会浪费宝贵的内存。

不要阻塞主线程

永远不要在主线程上执行繁重的操做,由于 UIKit 在主线程上执行本身全部的工做,这样会使页面发生卡顿,严重影响用户体验。

大部分阻塞主线程的状况发生在执行I/O操做时,由于该操做须要从外部资源(如:磁盘、网络)读取或写入。

若是须要执行昂贵的操做,可使用 GCD 或 NSOperations 和 NSOperationQueues。

根据图像视图大小来调整图像大小

若是使用 UIImageView 来显示图像,要确保图像和 UIImageView 的大小相同。动态缩放图像会很是昂贵,特别是将 UIImageView 嵌入到 UIScrollView 中。

若是是从远程服务器下载的图像,有时可能没法控制大小。那么,能够在图像下载完成后,在后台线程中手动缩放图像,而后在 UIImageView中使用调整过大小的图像。

正确选择集合类

学习使用最合适的类或对象来完成手头的任务是编写高效代码的基础。在处理集合时更是如此。一下时常见集合类型的简要说明:

  • Arrays:有序列表,按索引查找速度快,按值查找速度慢,插入删除速度慢。
  • Dictionaries:存储键值对儿。按键进行快速查找。
  • Sets:无序列表。按值快速查找,快速插入/删除。

启用 gzip 压缩

大部分应用程序都依赖于来自远程服务器或其余外部 api 的外部数据。在某个时候,您的应用程序,须要下载XML、JSON、HTML或其余文本格式的数据。

问题是,当涉及到移动设备时,不能依赖网络情况。用户能够在一分钟内到达边缘网络,而后进入3G网络。不论是什么状况,你都不想让你的用户等待!

减小文件大小和加快基于网络的资源下载的一个选项是在服务器和客户端上都启用 gzip 压缩。这对于基于文本的数据尤为有用,由于它具备很高的潜在压缩比。

中级技巧

重用和懒加载视图

更多的视图意味着更多的绘制,这意味着更多的 CPU 和内存开销。若是在 UIScrollView 中嵌入了不少视图,这一点尤其重要。

管理它的技巧时模仿 UITableView 和 UICollectionView 的行为:不要一次建立索引子视图,而是根据须要建立视图,并在完成时将其添加到重用队列中。这样,您只须要在执行滚动时配置视图,从而避免分配成本—这可能会很昂贵。

建立视图的时机至少有如下两种方法:

  • 在页面第一次加载时建立视图并将其所以,在须要时在显示该视图。
  • 在须要时建立并显示视图。

这两种方法各有利弊:

第一种方法会消耗更多的内存,由于会当即建立一个视图,该视图在释放以前一直保留在内存中。可是,当须要显示该视图时,由于只须要更改视图可见性,所以会更快显示出视图。

第二张方法会是相反的效果。只在须要时建立视图,会消耗更少的内存。可是,在须要显示时,视图显示不会那么及时。

缓存

在开发应用程序时,一个很好的经验法则是“缓存重要的东西”,也就是那些不太可能改变但常常访问的东西。如:UITableViewCell 的行高,远程服务器的响应等。

考虑使用绘制

在 iOS 中,有几种方法能够制做漂亮的按钮。可使用全尺寸图像,可调整大小的图像,也可使用 CALayer、CoreGraphics设置OpenGL手动绘制。

使用图片会更快,由于没必要建立图像并在其上绘制形状,就能够最终显示到屏幕上。问题是须要将这些图片放入到项目中,会增长包大小。

考虑什么对你最重要:绘制性能或包大小,而后选择合适的方案。

处理内存警告

当系统内存不足时,iOS 会通知全部正在运行的应用程序。若是你的应用收到此警告,它必须释放尽量多的内存。最好的方式是:移除缓存,图像对象,和稍后可从新建立的数据对象的强引用。

UIKit 提供了几种接收低内存警告的方法:

  • 实现应用程序的 applicationDidReceiveMemoryWarning: 代理方法。
  • 自定义 UIViewController 的子类中,重写 didReceiveMemoryWarning方法。
  • 注册 UIApplicationDidReceiveMemoryWarningNotification 通知。

一旦收到内存警告,释放全部可能的内存是很是重要的。不然,您的应用程序可能会被系统杀死。

在开始剔除对象以释放内存时要当心,由于须要确保之后能够从新建立它们。在开发应用程序时,务必使用 iOS 模拟器上的模拟内存警告功能来测试此状况。

重用昂贵的对象

有些对象的初始化速度很是慢,如 NSDateFormatter 和 NSCalendar 。可是,你不能老是避免使用它们,例如在解析 json/xml 响应中的日期。

为了不在使用这些对象时出现性能瓶颈,要尽量重用这些对象。能够经过向类中添加属性或静态变量来完成此操做。

注意,若是你选择第二种方法,则当应用程序运行时,对象将保留在内存中,这与单例很是相似。

避免从新处理数据

大部分应用程序都须要从远程服务器获取数据,这些数据一般以 json 或 xml 的格式出现。在请求和接收时,在两端使用相同的数据结构是重要的。由于,操做内存中的数据以适应新的数据结构会是昂贵的。

例如,若是须要在表视图中显示数据,最好以数组格式请求和接收数据,以免对数据进行任何中间操做,使其适合你须要的数据结构。

相似地,若是应用程序依赖于经过键访问特定值,那么你须要请求并接收字典。

选择正确的数据格式

有多种方法能够将数据从服务器传输到应用程序,但最多见的两种方法是 JSON 和 XML 。你要确保为你的应用选择正确的一个。

JSON 的解析速度更快,并且一般比 xml 小,这也意味着传输更少的数据。并且自从 iOS 5以来,就有了内置的 JSON 反序列化,所以也很容易使用。

XML 的一个有点是,若是使用 SAX 解析方法,能够在脱机状态下使用 XML 数据,而没必要等到整个数据到达后在像 JSON 那样解析它。处理很是大的数据集时,这能够提升性能并减小内存消耗。

适当设置背景图

至少有两种方法能够将背景图像放置在视图中:

  • 能够将背景色设置为使用 UIColor 的 colorWithPatternImage: 方法建立的颜色。
  • 能够将 UIImageView 子视图添加到视图中。

若是您有一个全尺寸的背景图像,那么必定要使用 UIImageView ,由于 UIColor 的 colorWithPatternImage: 建立的是小图,而不是大尺寸图像。在这种状况下,使用 UIImageView 将节省大量内存。

若是视图背景使用较小的图像,这些图像将被重复或平铺以填充背景,您应该使用 UIColor 的 colorWithPatternImage: 代替,由于在这种状况下,绘制速度更快,并且不会占用大量内存。

设置阴影路径

当要给视图添加阴影时,大部分开发人员会向下面这样:

UIView *view = [[UIView alloc] init];
view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f);
view.layer.shadowRadius = 5.0f;
view.layer.shadowOpacity = 0.6;
复制代码

这种方法是有问题的, Core Animation必须进行一次离屏操做,以肯定视图的确切形状,而后才能渲染阴影,这个操做是很是昂贵的。

有一个系统更容易渲染的替代方案:设置阴影路径。

view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];
复制代码

经过设置阴影路径,iOS 不须要从新计算它应该如何绘制阴影。相反,它会使用一个预先计算好的路径。坏消息是,根据你的视图样式,可能很难由你本身计算路径。另外一个问题是,每次视图的帧发生更改时,都须要更新阴影路径。

优化表视图

要使表视图滚动顺畅,确保实现如下建议:

  • 经过设置正确的 reuseIdentifier 来重用单元格。
  • 使尽量多的视图不透明,包括单元格自己。
  • 避免渐变、图像缩放和离屏渲染。
  • 缓存行的高度,若是它们不老是相同的话。
  • 若是单元格显示来自 Web 的内容,请确保异步调用并缓存响应。
  • 使用 shadowPath 来设置阴影。
  • 减小子视图数量。
  • 尽量少的在 cellForRowAtIndexPath: 中工做。若是你须要作一些工做,只作一次并缓存结果。
  • 使用适当的数据结构保存所需的信息。不一样的结构对不一样的操做有不一样的成本。
  • 使用 rowHeight 、sectionFooterHeight 和 sectionHeaderHeight 来设置恒定高度,而不是经过询问代理得到。

选择正确的数据存储选项

在存储和读取大型数据时,会有以下选择:

  • 使用 NSUserDefaults。
  • 以 XML 、 JSON 或 Plist 的格式保存到结构化文件。
  • 使用 NSCoding 归档。
  • 保存到本地 SQL 数据库,如 SQLite 。
  • 使用 Core Data 。

在保存的数据量很小的时候,可使用 NSUserDefaults 。

保存到结构化文件,须要先将整个文件加载到内存中,而后才能对其进行解析,这是一个昂贵的操做。你可使用 SAX 处理 XML 文件,这是一个复杂的解决方案。一样,无论你想不想让全部的对象都加载到内存中。

NSCoding 也须要读取和写入文件,会遇到和上面相同的问题。

最好使用 SQLite 或 Core Data。使用这些技术,您能够执行特定的查询以仅加载所需的对象,并避免使用暴力搜索方法来检索数据。在性能方面,SQLite 和 Core Data 很是类似。

SQLite 和 Core Data 之间的最大区别在于它们的用法。Core Data表示一个对象图模型,而 SQLite 只是一个普通的 DBMS。一般苹果建议你使用 Core Data ,可是若是你有特殊的缘由想避免它,你可使用更底层的 SQLite 。

高级技巧

加快启动时间

快速的启动应用程序很是重要,尤为当用户第一次启动时。第一印象对应用程序来讲意义重大。

要使应用程序快速的启动,能够作的最大的事情是执行尽量多的异步任务,如网络请求、数据库访问或解析数据。

另外,尽可能避免臃肿的 XIBs ,由于它是运行在主线程上的。

Autorelease Pool

NSAutoreleasePool 负责释放块内的自动释放对象。一般,它是由 UIKit 自动调用的。但在某些状况下,可能须要手动建立 NSAutoreleasePools 。

若是在代码中建立了许多临时对象,则会注意到在释放这些对象以前内存使用量会增长。问题是,只有在 UIKit 销毁其自动释放池以后,才会释放该内存,这意味着该内存的保存时间要比须要的长。

能够经过在本身的@autoreleasepool块中建立这些临时对象来避免这种状况。

是否缓存图像

加载 UIImage 有两种常见的方法,第一种是使用 imageNamed , 第二种是使用 imageWithContentsOfFile 。

imageNamed 的优势是在加载时缓存图像。而 imageWithContentsOfFile 不会缓存。

若是要加载只使用一次的大图像,则无需缓存该图像。在这种状况下,imageWithContentsOfFile 能够很好地知足需求。这样,操做系统就不会浪费内存来缓存图像。

imageNamed 对于在应用程序中重用的图像来讲是一个更好的选择。这样,操做系统就节省了不断从磁盘加载图像的时间。

尽可能避免使用 Date Formatters

若是有不少日期须要用 NSDateFormatter 解析,须要当心处理。如前所述,尽量重用 NSDateFormatters 是一个好建议。

若是能够控制要处理的日期的格式,请尽量选择 Unix 时间戳。Unix 时间戳是简单的整数。


关注公众号:iOS学习社区,阅读更多技术好文