更多内容请挪步我的博客
之前写过一篇关于性能的文章,最近又需要优化性能,所以在此基础上写一篇最近的优化总结
性能优化的几个方面:内存问题、电量消耗、启动时间、执行与响应速度、网络条件等等,下面就从这几个方面讨论下如何优化
内存问题
可借助工具:Instruments -> Leaks / Allocations / Zombies
内存问题,主要包括避免内存泄漏(使用 ARC,Block 中使用变量不要循环引用,可以借鉴以前的文章)、及时释放可以释放的无用内存(例如在一个循环中处理图片,可以使用自动释放池)
电量消耗
可借助工具:Instruments -> Energy Log
当 CPU 占用率较高,使用网络/GPS 等情况下,电量会消耗较快。
降低 CPU 使用率的方式是优化代码中的算法,能使用系统 API 的尽量使用系统 API,这要求我们对不常用的 API 要有所了解,另外多看别人的源代码,对于不了解的 API 要查询下用法,算法方面,除了好脑子就要靠平时多看算法类的书籍锻炼了。
网络问题见下详述
GPS检查,设置合适的处理精度和距离,使用合适的 API 来更新位置。
例如在非实时(追踪走路等)定位的场景下,调用如下方法延时获取数据
[locationManager allowDeferredLocationUpdatesUntilTraveled:timeout:]
或者位置有重大变化(天气应用)时才需要获取数据,调用如下方法
[locationManager startMonitoringSignificantLocationChanges]
再或者使用区域检测
[locationManager startMonitoringForRegion:]
启动时间
执行与响应速度
可借助工具:Instruments -> Time Profiler
通过 Time Profiler 可以找到调用时间较长的 API,对消耗时间较长的代码进行优化,找到可优化代码只是第一步,比较困难的可能是如何优化,下面就几个方面讨论下优化途径
缓存
常用的小图片可以用内存的方式加载 [UIImage imageName:] 方法
列表滚动需要缓存高度
部分常用的数据也可以放到缓存中,需要考虑何时更新缓存以及删除缓存的时机
多线程
非 UI 操作是否可以放到线程中,例如大图片的解压,文件的保存( GCD 创建 dispatch_queue 指定优先级的时候,有一个 DISPATCH_QUEUE_PRIORITY_BACKGROUND 级别,适合写文件),数据的处理等。另外多线程中处理的数据最好是 immutable 不可变的。
另外创建线程也是有开销的,成本主要是构造内核数据结构( 1KB 左右),栈空间(主线程 1M 左右,子线程 512K),创建线程大约需要消耗 90 毫秒
代码优化
优化过程中如果发现相同代码被复制粘贴过多次的话,一定顺手将其改掉,合并为一处代码
是否有不必要的循环语句在执行,是否有对象重复创建其实使用一个创建的对象即可;如果循环中查找到结果是否在必要的地方写了 break;
GPU 离屏渲染等优化
可借助工具:Instruments -> Core Animation
数据库查询优化
如果使用了数据库,那么数据库的操作是一个很容易导致响应时间问题的罪魁祸首
看下经常查找的字段上是否建立了索引,注意的是建立索引的字段上是否使用了 BETWEEN OR LIKE NULL 等会导致全文查找的条件,索引失效
WHERE 条件,过滤大数据的条件应写到最后
由于 SQL WHERE 分析和执行顺序是从右到左的,所以可以某个条件可以过滤掉大部分数据的话,这样的条件应该写在最后
例如 SELECT * FROM TABLE Where a = '1' and b = '1', 表中 b 列值为 1的数据比较少时 应当 b 写到最后,因为先执行 b = '1' 的判断
LIKE 可以使用 MATCH 替换
http://www.jianshu.com/p/854f0d3fa240
另外要查找是否有地方使用了 SQL 查找和内存查找并存,但是可以都使用 SQL 替代实现的代码
最后,应当对 SQL 执行时间超过 5ms 的操作予以记录,我们现在使用的日志库是 CocoaLumberjack,比较好用,并在这个库的基础上做了一些调整,专门用于记录需要优化的部分执行时间。
其他
对常用的数据合理的使用缓存
使用懒加载的方式处理非必要存在的变量,例如某个 View,只有在某个操作下才会显示,可以使用懒加载的方式创建
是否有不必要的读写文件过程,可以放到内存中即可。如果需要读写文件,确认是否可以使用线程,是否可以使用缓存,是否可以将多次写入合并写入。
重大开销对象,例如 NSDateFormatter和 NSCalendar,如果频繁使用,可以创建单例
使用更优的 API 和合适的数据类型,例如无序且没有重复数据的数组可以替换为集合 NSSet,集合比数组在检索上效率更高,由于集合上的数值都有 hash 值,检索时都会操作哈希表来进行优化,所以相对于 NSArray 中的 containsObject、indexOfObject、removeObject 等复杂度大于等于 N 的操作,使用 NSSet 其时间复杂度都为 1
如有有些操作确实比较耗时,又无法优化,可以和 UI UE 商量下如何制作出一个动效,好的动效可以让用户察觉不到相应速度的问题
网络
可借助工具:Instruments -> Network
请求网络时判断当前的网络状态,不同网络状态下获取不同数据,例如图片大小要不一样等。如果请求失败,可以记录下失败的请求地址,再尝试下载的时候应间隔时间长一些
另外,手机 “设置 -> 开发者 -> NetWork Link Conditioner” 可以模拟网络环境很差的情况,便于调试。