余额不足.

iOS性能优化-能源篇

字数统计: 1.5k阅读时长: 5 min
2020/03/26 Share

WWDC2017有一篇关于节省电池能源的分享,视频中表示图中的四兄弟就是吃掉大量电池能源的罪魁祸首:
1、CPU的处理工作
2、网络
3、定位
4、图形处理

网络的消耗

  • 数据加载
    现今市面上的App利用网络无非是为了加载数据、上传图片/文件以及上报用户分析数据(埋点)。
    早些年可能有部分App加载数据可能是用定时器操作,经过固定的一段时间就拉取一次数据。这样的操作方式需要CPU执行一个定时器,然后每一段时间就需要加载一次网络请求,这样的开销可想而知。

  • 上传图片/文件
    可能目前有些App的操作方式是拿到上传任务就马上开始进行图片/文件上传,如果失败或者超时了就进行重试,这样的操作方式下大概的能源消耗如图所示:

解决方案

1、按需进行网络请求
如今大部分的App都配备了下拉刷新的功能,如果用户有需要更新页面数据话直接进行手动下拉即可完成网络数据加载,这样即可不需要维护一个timer已经固定时间的网络请求消耗。
2、减少重试次数
3、设定合理的超时时间、不然网络服务一直挂载着同样会造成高额的消耗
4、上传图片/文件时可采取批量操作减少开销,且可以将上传队列使用UTILITY的QOS优先级
5、推荐使用NSURLSession和BackGround session,因为他们具备了WaitsForConnectivity(等待网络可用时再执行任务)、缓存、自动重试、吞吐量监控的能力

定位

定位耗电是众所周知的了吧,就不过多赘述

解决方案

1、明确你想要的定位精度水平,精度越高越耗电
2、尽量避免使用连续定位的方法
3、如果定位已经不再需要使用的时候请关闭定位
4、延迟定位更新

图形处理

一般情况下,App耗电最大的两个单位就是CPU与GPU,CPU负责计算,GPU负责展示CPU计算好的数据,这些工作是不可避免的,所以官方推荐我们的最佳实践如下。

解决方案

1、避免模糊不清的用法
2、尽量减少屏幕元素的更新

CPU工作的消耗

一上来就要面对这个严重的问题感觉体验有点不太友好啊,所以苹果在WWDC把这个问题放在了最后说明

解决方案

1、减少后台的操作
执行不必要的后台活动会产生一定的能量消耗,苹果的官方性能文档中提到了一些常见后台浪费能源的原因:

1
2
3
4
5
6
7
8
9
10
Not notifying the system when background activity is complete
当活动完成的时候没有通知系统
Playing silent audio
播放无声的音频
Performing location updates
定位更新
Interacting with Bluetooth accessories
与蓝牙设备的交互
Downloads that could be deferred
后台时依然在下载

文档中推荐我们的app进入后台时关闭一些不必要的操作、延迟下载操作等。
在调用定位操作时,尽量不要使用startUpdatingLocation,即使使用了也尽可能迅速的调用stopUpdatingLocation停止定位服务。

2、使用优先级低的队列
这里不是让你所有的任务都派发在低优先级的队列中,而是可以将一些耗时的操作放入优先级低的队列中。
由于高优先级的工作比低优先级的工作执行的更快,所占用的资源更多,因此与低优先级的工作相比,通常需要耗费更多的能源。
戴铭老师的深入剖析iOS性能优化中提到了:
将 GCD 的 block 通过 dispatch_block_create_with_qos_class 方法指定队列的 QoS 为 QOS_CLASS_UTILITY。这种 QoS 系统会针对大的计算,I/O,网络以及复杂数据处理做电量优化。

1
2
3
4
5
6
7
8
9
10
* @constant QOS_CLASS_UTILITY
* @abstract A QOS class which indicates work performed by this thread
* may or may not be initiated by the user and that the user is unlikely to be
* immediately waiting for the results.
* @discussion Such work is requested to run at a priority below critical user-
* interactive and user-initiated work, but relatively higher than low-level
* system maintenance tasks. The use of this QOS class indicates the work
* should be run in an energy and thermally-efficient manner. The progress of
* utility work may or may not be indicated to the user, but the effect of such
* work is user-visible.

通过查询libpthread的源码,有这样的一段注释提到了使用QOS_CLASS_UTILITY会采用高效而节能的方式完成所派发的任务。

最佳实践:Alamofire

1
2
3
4
5
6
7
8
9
10
11
TaskDelegate.swift
init(task: URLSessionTask?) {
_task = task
self.queue = {
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
operationQueue.isSuspended = true
operationQueue.qualityOfService = .utility
return operationQueue
}()
}

SessionManager.swift中的upload操作同样也适用了.utility,代码太长就不贴了。

使用方法
OC:

1
2
3
4
5
6

dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
dispatch_queue_t q = dispatch_queue_create("queue.name", attr);
dispatch_async(q, ^{
// do sth
});

Swift:

1
2
3
4
let q = DispatchQueue(label: "queue.name", qos: .utility, attributes: .concurrent, autoreleaseFrequency: .inherit, target: .none)
q.sync {
// do sth
}

3、I/O 性能优化
当触发I/O相关操作时都会使系统进入高功耗状态,所以在使用I/O操作时请尽量注意以下几点:

  1. 不要过于频繁的访问内存,如果是需要写入状态信息请等待状态完毕再进项写入,如果是需要写入若干零碎的文件,请尝试将零碎的文件合并后再写入。
  2. 要读取或写入大量数据,请考虑使用dispatch_io,它提供了基于GCD的异步API来执行文件I/O。
  3. 尽可能的按顺序进行I/O操作,因为寻找读取和写入的位置都是耗时的操作。
  4. 针对数据类型分析是应该存入缓存或者数据库

4、干掉那些该死的timer
timer的存在会一直占用着cpu资源,让cpu不能处于idle状态,而idle状态下的功耗是最低的,所以能不实用timer就尽量不使用它。

CATALOG
  1. 1. 网络的消耗
    1. 1.1. 解决方案
  2. 2. 定位
    1. 2.1. 解决方案
  3. 3. 图形处理
    1. 3.1. 解决方案
  4. 4. CPU工作的消耗
    1. 4.1. 解决方案