iOS性能优化详解

iOS性能优化详解

在性能优化中一个具有参考价值的属性FPS:Frames Per Second ,其实就是刷新屏幕率。苹果推荐的推荐率是60FPS,也就是说GPU每秒刷新的频率是60次。FPS值的大小提现了页面流畅程度高低。当低于45的时候,页面会有明显的卡顿。

图层混合

每一个 Layer 是一个纹理,所有的纹理都以某种方式堆积在屏幕的顶部。对于屏幕上的每一个像素,GPU都需要算出怎么混合这些纹理来得到像素的RGB值。

一、入门级

  1. 用ARC管理内存

  2. 在正确的地方使用reuseIdentifier

  3. 劲量把 Views 设置为透明

    如果你有透明的 Views 你应该设置它们的 opaque 属性为 YES。系 统用一个最优的方式渲染这些 views。这个简单的属性在 IB 或者代码里都可以设定

  4. 避免过于庞大的Xib

    尽量简单的为每个 Controller 配置一个单独的 XIB,尽可能把一个 View Controller 的 view 层次结构分散到单独的 XIB 中去, 当你加载一个引用了图片或者声音资源的nib 时, nib 加载代码会把图片和声音文件写进内存,会消耗不必要的内存。

  5. 不要阻塞主线程

    永远不要使主线程承担过多。因为 UIKit 在主线程上做所有工作,渲染,管理 触摸反应,回应输入等都需要在它上面完成,大部分阻碍主进程的情形是你的 app 在做一些牵涉到读 写外部资源的 I/O 操作,比如存储或者网络。

  6. 在ImageViews中调整图片大小。

    如果要在UIImageView中显示一个来自bundle的图片,你应该保证图片的大小跟UIImageView的大小相同。在运行中缩放图片的大小是很费资源的,特别是UIImageView嵌套在UIScrollView中的情况。如果图片是从服务器端下载的,不能控制图片的大小,比如在下载前调整到合适的大小,在下载后用background thread 缩放一次,然后在UIImageView中使用缩放后的图片。

  7. 选择正确的Collection

    Arrays: 有序的一组值。使用 index 来 lookup 很快,使用 value lookup 很慢, 插入/删除很慢
    Dictionaries: 存储键值对。 用键来查找比较快。
    Sets: 无序的一组值。用值来查找很快,插入/删除很快。

  8. 打开 gzip 压缩。

    app 可能大量依赖于服务器资源,问题是我们的目标是移动设备,因此你就不能指望网络状况有多好。减小文档的一个方式就是在服务端和你的 app 中打开 gzip。这对于文字这种能有更高压 缩率的数据来说会有更显著的效用。 iOS 已经在 NSURLConnection 中默认支持了 gzip 压缩,当然 AFNetworking 这些基于它的框架亦然

二、中级

  1. 重用和延迟加载(lazy load)Views

    更多的view意为着更多的渲染,也就更消耗CPU的内存。对于那种嵌套了很多Views的UIScrollView更是如此。
    这里我们用的技巧就是模仿UITableVIew和UICollectionView的操作:不要一次创建所有subviews,而是当需要的时候创建,完成使命后,放入到一个可重用的队列,这样就只有在滚动的时候去穿件Views,避免不划算的内存分配。

  2. Cache

    一个极好的原则,就是缓存所需,也就是那些不经常改变,但是却常用到的东西。
    我们能缓存些什么呢 ?网络下载的图片,数据,计算结果。例如Cell的行高。
    NSCache和NSDictionary类似,不同的是系统回收内存的时候,它会自动删除里面的内容。

  3. 权衡渲染方法。

  4. 处理内存警告。

    AppDelegate代理 applicationDidReceiveMemoryWarning

  5. 重大开销对象

  6. 一些 objects 的初始化很慢,比如 NSDateFormatterNSCalendar 。然而又不能不用他们,比如从网络获取数据 JSON 或者XML,就需要重用他们,将他们添加的类的属性或者使用静态变量。

  7. 避免反复处理相同的数据。在服务端跟客户端用相同的数据结构很重要。

  8. 选择正确的数据格式。解析 JSON 比 XML 要快一些。

  9. 正确设定背景图片。

    全屏背景图,在View添加一个UIImageView作为一个子View
    某个小View,就需要用到UIColor的 colorWithPatternImage,它会更快的渲染,不会花费很多内存。

  10. 减少使用 Web 的特性

    想要更高的性能你就要调整下你的 HTML 了。第一件要做的事就是尽可能移除 不必要的 javascript,避免使用过大的框架。能只用原生 js 就更好了。尽可能异步加载例如用户行为统计 script 这种不影响页面表达的 javascript。注意你使用的图片,保证图片的符合你使用的大小。

  11. Shadow Path

    CoreAnimation 不得不先在后台得出你的图形并加好阴影然后才渲染,这开销是很大 的。使用 shadowPath 的话就避免了这个问题。使用 shadow path 的话 iOS 就不必每次都计算如何渲染, 它使用一个预先计算好的路径。但问题是自己计算 path 的话可能在某些 View 中比较困难,且每当 view 的 frame 变化的时候你都需要去 update shadow path.

  12. 优化 TableView

    正确使用 reuseIdentifier 来重用 cells
    尽量使所有的 view opaque,包括 cell 自身
    避免渐变,图片缩放,后台选人
    缓存行高
    异步加载
    使用shadowPath画阴影
    减少subviews的数量
    尽量不适用 cellForRowAtIndexPath:,如果你需要用到它,只用-一次然后缓存结果
    使用正确的数据结构来存储数据
    使用 rowHeight, sectionFooterHeightsectionHeaderHeight 来设定固定的高,不要请求 delegate

  13. 选择正确的数据存储选项

    NSUserDefaults 的问题是什么?虽然它很 nice 也很便捷,但是它只适用于小数据,比如一些简单的 布尔型的设置选项,再大点你就要考虑其它方式了。
    XML 这种结构化档案呢?总体来说,你需要读取整个文件到内存里去解析,这样是很不经济的。使用 SAX 又是一个很麻烦的事情。
    NSCoding?不幸的是,它也需要读写文件,所以也有以上问题。
    在这种应用场景下,使用 SQLite 或者 Core Data 比较好。使用这些技术你用特定的查询语句就能只加载你需要的对象。
    在性能层面来讲,SQLite 和 Core Data 是很相似的。他们的不同在于具体使用方法。
    Core Data 代表一个对象的 graph model,但 SQLite 就是一个 DBMS
    Apple 在一般情况下建议使用 Core Data,但是如果你有理由不使用它,那么就去使用更加底层的 SQLite 吧。
    如果你使用 SQLite,你可以用 FMDB 这个库来简化 SQLite 的操作,这样你就不用花很多经历了解 SQLite 的 C API 了。

三、高级

  1. 加速启动时间。

    你能做的就是使它尽可能做更多的异步任务,比如加载远端或者数据库数据,解析数据。避免 过于庞大的 XIB,因为他们是在主线程上加载的。所以尽量使用没有这个问题的 Storyboards 吧!一定要 把设备从 Xcode 断开来测试启动速度。

  2. 使用 Autorelease Pool

    NSAutoreleasePool负责释放 block 中的 autoreleased objects。一般情况下 它会自动被 UIKit 调用。但是有些状况下你也需要手动去创建它。假如你创建很多临时对象,你会发现内 存一直在减少直到这些对象被 release 的时候。这是因为只有当 UIKit 用光了 autorelease pool 的时候 memory 才会被释放。消息是你可以在你自己的@autoreleasepool 里创建临时的对象来避免这个行为。

  3. 选择是否缓存图片。

    常见的从 bundle 中加载图片的方式有两种,一个是用 imageNamed,二是用 imageWithContentsOfFile,第一种比较常见一点。

四、光栅化

光栅化是将几何数据通过一些列变化最终转化为像素,从而呈现到显示屏幕上的过程。光栅化的本质是左边转换,几何离散化。

我们使用 UITableView 和 UICollectionView 时经常会遇到各个 Cell 的样式是一样的,这时候我们可以使用这个属性提高性能:

 cell.layer.shouldRasterize = YES;
 cell.layer.rasterizationScale = [[UIScreen mainScreen]scale];   

五、如何检测内存泄露

  • Memory Leaks
  • Alloctions
  • Analyse
  • Debug Memory Graph
  • MLeaksFinder

六、如何高性能的画一个圆角

视图和圆角的大小对帧率没有影响,数量才是影响的核心。

    self.layer.cornerRadius = UMXFrom6(10);
    self.layer.masksToBounds = YES;

上面这种发放是不可取的,会出发离屏渲染

  • 如果只用 cornerRadius 解决问题,就不用优化。
  • 如果必须设置 masksToBounds ,则考虑圆角的数量。如果较少也可以考虑不用优化。
  • UIImageView 的圆角通过直接截取图片实现,其它视图的圆角可以通过 Core Graphics 画出圆角矩形实现。

七、如何提升 tableview 的流畅度

本质上是降低CPU,GPU的工作,从这两个大的方面去提升性能。

  • CPU :对象的创建和销毁,对象属性的调整,布局计算,文本计算和排版,图片格式的转换和解码,图像的绘制。
  • GPU:纹理的渲染。
卡顿优化在CPU层
  • 尽量使用轻量级的对象,比如用不到事件处理的地方,用 UILayer 替代UIView。
  • 不要频繁的调用 UIView 相关的属性,比如frame,bouns,transform 等属性,尽量减少不必要的修改。
  • 尽量提前计算好布局,在有需要的时一次性调整对应的属性,不要多次修改。
  • 图片的 Size 最好跟 UIImageView 的 Size 保持一致。
  • 控制一下线程的最大并发数。
  • 尽量吧耗时操作放到子线程。
卡顿优化在GPU层
  • 尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示
  • GPU能处理的最大纹理尺寸是4096x4096,一旦超过这个尺寸,就会占用CPU资源进行处理,所以纹理尽量不要超过这个尺寸
  • GPU会将多个视图混合在一起再去显示,混合的过程会消耗CPU资源,尽量减少视图数量和层次
  • 减少透明的视图(alpha<1),不透明的就设置opaque为YES,GPU就不会去进行alpha的通道合成
  • 尽量避免出现离屏渲染.
  • 合理使用光栅化 shouldRasterize:光栅化是把GPU的操作转到CPU上,生成位图缓存,直接读取复用。CALayer会被光栅化为bitmap,shadows、cornerRadius等效果会被缓存。更新已经光栅化的layer,会造成离屏渲染。bitmap超过100ms没有使用就会移除。受系统限制,缓存的大小为 2.5X Screen Size。shouldRasterize 适合静态页面显示,动态页面会增加开销。如果设置了shouldRasterize为 YES,那也要记住设置rasterizationScale为contentsScale。
  • 异步渲染.在子线程绘制,主线程渲染。

八、如何优化 App 电量

程序的耗电主要在以下四个方面:

  1. CPU 处理
  2. 定位
  3. 网络
  4. 图像

优化途径主要体现在以下几方面:

  • 尽量降低CPU,GPU 的功耗。

  • 尽量少用定时器

  • 优化 I/O 操作

    (1)不要频繁写入小数据,而是积攒到一定数量一起写入。
    (2)读写大量数据的时候可以使用 Dispatch_io, GCD 内部已经做了优化。
    (3)数据量较大时,建议使用数据库。

  • 网络方面的优化

    (1)减少压缩数据网络。(XML–>JSON–>ProtoBuf),如果可能建议使用ProtoBuf
    (2)如果请求返回的数据相同,可以使用 NSCache 缓存。
    (3)使用断电续传,避免因网络失败后要重新下载。
    (4)网络连接断开后,不要尝试网络连接。
    (5)长时间的网络连接,需要提供取消的操作。
    (6)采取批量传输。下载视频流的时候,尽量一大块一大块的进行下载,广告可以一次下载多个。

  • 定位层面的优化

    (1)如果只是需要快速定位,最好使用 CLLocationManager 中的requestLocation 方法,定位完成后会自动让硬件断电。
    (2)如果不是导航应用,定位完成后就应该关掉定位服务。
    (3)尽量降低地位精度。
    (4)需要后台定位时,尽量设置 pausesLocationUpdatesAutomatically 为 YES,如果用户 不太可能移动的时候系统会自动暂停位置更新。
    (5)尽 量 不 要 使 用 startMonitoringSignificantLocationChanges , 优 先 考 虑 startMonitoringForRegion:

  • 硬件检测优化

    用户移动、摇晃、倾斜设备时,会产生动作(motion)事件,这些事件由加速度计、陀螺仪、 磁力计等硬件检测。在不需要检测的场合,应该及时关闭这些硬件

九、如果有效的降低 APP 包的大小?

降低包大小需要从两方面着手

可执行文件
  • 编译器优化

    (1) Strip Linked Product、Make Strings Read-Only、Symbols Hidden by Default 设置为 YES
    (2) 去掉异常支持,Enable C++ Exceptions、Enable Objective-C Exceptions 设置为 NO, Other C Flags 添加 -fno-exceptions

  • 利用 AppCode 检测未使用的代码:菜单栏 -> Code -> Inspect Code

  • 编写 LLVM 插件检测出重复代码、未被调用的代码

资源

资源包括 图片、音频、视频 等

  • 可对资源进行无损压缩。
  • 去除没有用到的资源。

十、什么是离屏渲染?什么情况下会出发?该如何应对

离屏渲染:就是当前屏幕缓冲区以外,新开辟缓冲区进行操作。

离屏渲染触发的场景

  • 圆角(MaskToBouns 并用才会触发)
  • 图层模板
  • 阴影
  • 光栅化

为什么要避免离屏渲染?

CPU GPU 在绘制渲染视图时,做了大量的工作。离屏渲染发生在GPU层面,会创建新的渲染缓冲区,会出发 OpenGL 的多通道渲染管线,图形上下文切换会造成额外的开销,增加 GPU 工作量,如果CPU GPU 累计耗时16.67秒还没有完成,则会造成页面卡顿。

圆角属性、蒙层遮罩 都会触发离屏渲染。指定了以上属性,标记了它在新的图形上下文中,在未愈合之前, 不可以用于显示的时候就触发了离屏渲染。

在 OpenGL 中,GPU 有 2 种渲染方式

  1. On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作.
  2. Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作.

离屏渲染消耗性能的原因

(1) 需要创建新的缓冲区
(2) 离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏 (Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文 环境从离屏切换到当前屏幕.

哪些操作会触发离屏渲染?

(1) 光栅化,layer.shouldRasterize = YES
(2) 遮罩,layer.mask
(3) 圆角,同时设置 layer.masksToBounds = YES、layer.cornerRadius 大于0。考虑通过 CoreGraphics 绘制裁剪圆角,或者叫美工提供圆角图片
(4) 阴影,layer.shadowXXX,如果设置了 layer.shadowPath 就不会产生离屏渲染
(5) edge antialiasing(抗锯齿)
(6) group opacity(不透明)
(7) 渐变
(8)drawRect

例如我们日程经常打交道的 TableViewCell,因为 TableViewCell 的重绘是很频繁的(因为 Cell 的复用),如果 Cell 的内容不断变化,则 Cell 需要不断重绘,如果此时设置了 cell.layer 可光栅化。则会造成大量的离屏渲染,降低图形性能。

如果将不在 GPU 的当前屏幕缓冲区中进行的渲染都称为离屏渲染,那么就还有另一种特殊的“离屏渲染” 方式:CPU 渲染

如果我们重写了 drawRect 方法,并且使用任何 Core Graphics 的技术进行了绘制操作, 就涉及到了 CPU 渲染。整个渲染过程由 CPU 在 App 内同步地完成,渲染得到的 bitmap 最后再交由 GPU 用于显示。

现在摆在我们面前得有三个选择:当前屏幕渲染离屏渲染CPU 渲染,该用哪个呢?这需要根据具体的 使用场景来决定。

尽量使用当前屏幕渲染

鉴于离屏渲染、CPU 渲染可能带来的性能问题,一般情况下,我们要尽量使用当前屏幕渲染。

离屏渲染 VS CPU 渲染

由于 GPU 的浮点运算能力比 CPU 强,CPU 渲染的效率可能不如离屏渲染;但如果仅仅是实现一个简单的效果,直接使用 CPU 渲染的效率又可能比离屏渲染好,毕竟离屏渲染要涉及到缓冲区创建和上下文切换等耗时操作

十一、如何检测离屏渲染?

  1. 模拟器 debug-选中 color Offscreen - Renderd 离屏渲染的图层高亮成黄可能存在性能问题
  2. 真机 Instrument-选中 Core Animation-勾选 Color Offscreen-Rendered Yellow
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页