一、启动优化
1、冷启动(从零开始的启动)
冷启动可以分为三个阶段
1.1 Main函数执行前
- 加载可执行文件(
mach-o文件
) - 加载动态链接库,进行
rebase
指针调整和bind
符号绑定 - Objc运行时的初始化处理,包括Objc相关类的
注册
、category注册
、selector
唯一性检查 - 初始化,包括执行了
+load()
方法、attribute((constructor))
修饰的函数调用、创建C++静态全局变量
优化方案
- 减少动态库加载。每个库本身都有依赖关系,苹果公司建议使用更少的动态库,并且建议在使用动态库的数量较多时,尽量将多个动态库进行合并。数量上,苹果公司建议最多使用 6 个非系统动态库。
- 减少加载启动后不会去使用的类或者方法
-
+load()
方法里的内容可以放到首屏渲染完成后再执行,或使用+initialize()
方法替换掉。 因为,在一个+load()
方法里,进行运行时方法替换操作会带来 4 毫秒的消耗。不要小看这 4 毫秒,积少成多,执行 +load() 方法对启动速度的影响会越来越大 - 控制
C++
全局变量的数量
1.2 Main函数执行后
主要是指main()函数执行开始,到Appdelegate
的didFinishLaunchingWithOptions
方法里首屏渲染相关方法的执行
- 首屏初始化所需要配置文件的读写操作
- 首屏列表大数据的读取
- 首屏渲染的大量计算
优化方案
从功能上梳理出哪些是首屏渲染必要的初始化功能,哪些是 App 启动必要的初始化功能,而哪些是只需要在对应功能开始使用时才需要初始化的。梳理完之后,将这些初始化功能分别放到合适的阶段进行。
1.3 首屏渲染完成
从渲染完成时开始,到 didFinishLaunchingWithOptions
方法作用域结束时结束
优化方案
-
功能级别优化
main() 函数开始执行后到首屏渲染完成前只处理首屏相关的业务,其他非首屏业务的初始化、监听注册、配置文件读取等都放到首屏渲染完成后去做 -
方法级别去优化
我们需要进一步做的,是检查首屏渲染完成前主线程上有哪些耗时方法,将没必要的耗时方法滞后或者异步执行。通常情况下,耗时较长的方法主要发生在计算大量数据的情况下,具体的表现就是加载、编辑、存储图片和文件等资源
2、热启动
- App在内存中,在后台存活着,再次点击图标进入App
3、APP启动的监控手段
- 1、定时抓取主线程上的方法调用堆栈,计算一段时间里各个方法的耗时
- 2、对
objc_msgSend
方法进行hook
来掌握所有方法的执行耗时。
二、卡顿问题
2.1 CPU和GPU
CUP(Central processing Unit,中央处理器)
- 对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(Core Graphics)
GPU (Graphics Processing Unit,图形处理器)
- 纹理的渲染
可以优化的点
- 尽可能减少CPU、GPU资源消耗
- 尽量用轻量级的对象,比如用不到事件处理的地方,可以考虑使用
CALayer
取代UIView
- 不要频繁地调用UIView的相关属性,比如
frame
、bounds
、transform
等属性,尽量减少不必要的修改 - 尽量提前计算好布局,在有需要时一次性调整对应的属性,不要多次修改属性
-
Autolayout
会比直接设置frame
消耗更多的CPU资源 - 图片的size最好刚好跟UIImageView的size保持一致
- 控制一下线程的最大并发数量
- 尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示
- GPU能处理的最大纹理尺寸是
4096x4096
,一旦超过这个尺寸,就会占用CPU资源进行处理,所以纹理尽量不要超过这个尺寸 - 尽量减少视图数量和层次
- 减少透明的视图
(alpha<1)
,不透明的就设置opaque为YES - 尽量把耗时的操作放到子线程(文本尺寸、图片处理)
2.2 离屏渲染(尽量避免出现离屏渲染)
- 在OpenGL中,GPU有2种渲染方式
-
On-Screen Rendering
:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作 -
Off-Screen Rendering
:离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作
2.3 离屏渲染消耗性能的原因
- 1、需要创建新的缓冲区
- 2、离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(
On-Screen
)切换到离屏(Off-Screen
);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕
2.4 哪些操作会触发离屏渲染?
- 光栅化,
layer.shouldRasterize = YES
- 遮罩,
layer.mask
- 圆角,同时设置
layer.masksToBounds = YES
、layer.cornerRadius大于0
(考虑通过CoreGraphics绘制裁剪圆角,或者叫UI提供圆角图片) - 阴影,
layer.shadowXXX
(如果设置了layer.shadowPath就不会产生离屏渲染)
三、耗电优化
- 1、尽可能降低CPU、GPU功耗
- 2、少用定时器
- 3、优化I/O操作
- 4、尽量不要频繁写入小数据,最好批量一次性写入
- 5、读写大量重要数据时,考虑用
dispatch_io
,其提供了基于GCD
的异步操作文件I/O的API。用dispatch_io
系统会优化磁盘访问 - 6、数据量比较大的,建议使用数据库(比如
SQLite
、CoreData
)
四、网络优化
- 1、减少、压缩网络数据
- 2、如果多次请求的结果是相同的,尽量使用缓存
- 3、使用断点续传,否则网络不稳定时可能多次传输相同的内容
- 4、网络不可用时,不要尝试执行网络请求
- 5、让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间
- 6、批量传输,比如,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。如果下载广告,一 次性多下载一些,然后再慢慢展示。如果下载电子邮件,一次下载多封,不要一封一封地下载
五、定位优化
- 1、如果只是需要快速确定用户位置,最好用
CLLocationManager
的requestLocation
方法。定位完成后,会自动让定位硬件断电 - 2、如果不是导航应用,尽量不要实时更新位置,定位完毕就关掉定位服务
- 3、尽量降低定位精度,比如尽量不要使用精度最高的
kCLLocationAccuracyBest
- 4、需要后台定位时,尽量设置
pausesLocationUpdatesAutomatically为YES
,如果用户不太可能移动的时候系统会自动暂停位置更新 - 5、尽量不要使用
startMonitoringSignificantLocationChanges
,优先考虑startMonitoringForRegion:
- 6、用户移动、摇晃、倾斜设备时,会产生动作(
motion
)事件,这些事件由加速度计、陀螺仪、磁力计等硬件检测。在不需要检测的场合,应该及时关闭这些硬件