一、启动优化
1.pre-main阶段
我们可以通过苹果提供了XCode内建的测量方法,
- 1.点击项目名称
- 2.
Edit scheme...
- 3.左侧
Run
- 4.中间顶部菜单
Auguments
- 5.在
Environment Variables
中添加一个环境变量DYLD_PRINT_STATISTICS
,并设为 1
再次运行项目,会得到以下输出
主要分为3个加载阶段
- 1.加载应用的可执行文件(app自身的所有.o文件集合)
- 2.加载动态链接器
dyld
(dynamic loader一个专门用来加载动态链接库的库) - 3.
dyld
递归加载应用所有需要的动态链接库
优化:
Apple官方视频讲解 https://developer.apple.com/videos/play/wwdc2016/406/
1.Load dylibs
一般iOS应用会加载100-400个dylibs,这些库都是系统的,并且Apple也做了很多的优化。
这一阶段主要就是分析应用以来的dylib(现在名为.tbd),找到其mach-o文件,然后验证其有效性,接着注册到内核,最后对dylib的每一个segment调用nmap()
2.Rebase/Bind
在dylib的加载过程中,系统为了安全考虑,引入了ASLR(Address Space Layout Randomization)技术和代码签名。由于ASLR的存在,镜像(Image,包括可执行文件、dylib和bundle)会在随机的地址上加载,和之前指针指向的地址(preferred_address)会有一个偏差(slide),dyld需要修正这个偏差,来指向正确的地址。
Rebase在前,Bind在后,Rebase做的是将镜像读入内存,修正镜像内部的指针,性能消耗主要在IO。Bind做的是查询符号表,设置指向镜像外部的指针,性能消耗主要在CPU计算。
我们在这一步可以做的优化有:
- 1.减少ObjC类(class)、方法(selector)、分类(category)的数量
- 2.减少C++虚函数的的数量(创建虚函数表有开销)
- 3.使用Swift structs(内部做了优化,符号数量更少)
3.ObjC Setup
大部分ObjC初始化工作已经在Rebase/Bind阶段做完了,这一步dyld会注册所有声明过的ObjC类,将分类插入到类的方法列表里,再检查每个selector的唯一性。
在这一步倒没什么优化可做的,Rebase/Bind阶段优化好了,这一步的耗时也会减少。
4.Initializers
到了这一阶段,dyld开始运行程序的初始化函数,调用每个Objc类和分类的+load方法,调用C/C++ 中的构造器函数(用attribute((constructor))修饰的函数),和创建非基本类型的C++静态全局变量(通常是类或结构体)。Initializers阶段执行完后,dyld开始调用main()函数。
Objc的load函数和C++的静态构造函数采用由底向上的方式执行,来保证每个执行的方法,都可以找到所依赖的动态库。例:
我们在这一步可以做的优化有:
- 1.少在类的+load方法里做事情,尽量把这些事情推迟到+initiailize
- 2.减少构造器函数的个数,在构造器函数里少做些事情
- 3.减少C++静态全局变量的个数
2.main阶段
主要分为4个加载阶段
- 1.
dyld
调用main()函数 - 2.调用
UIApplicationMain
函数 - 3.调用
applicationDidFinishLaunching
方法 - 4.调用
didFinishLaunchingWithOptions
方法
那么这些是我们可以通过代码来优化的地方,比如在一开始,不要初始化那么多的SDK或者功能,是否考虑一些初始化可以在子线程操作等
我们可以通过main阶段耗时加载多久
1.先在main函数这里设置当前时间
CFAbsoluteTime StartTime;
int main(int argc, char * argv[]) {
StartTime = CFAbsoluteTimeGetCurrent();
......
2.再在AppDelegate.m文件中用extern声明全局变量StartTime
extern CFAbsoluteTime StartTime;
3.最后在didFinishLaunchingWithOptions
里,再获取一下当前时间,与StartTime的差值即是main()阶段运行耗时。
double launchTime = (CFAbsoluteTimeGetCurrent() - StartTime);
NSLog(@"耗时 launchTime = %lf 秒", launchTime);
二、耗电优化
主要集中在
- 1.网络
Network
- 2.定位
- 3.蓝牙
- 4.app前后台
- 5.
CPU
计算 - 6.
GPU
渲染
1.定位功能
- app推到后台时,非导航功能之外,可以暂停定位功能,比如:把
pauseLocationUpdatesAutomatically = YES;
- 设置定位的精度越高,定位硬件模块功耗越大,如果不是要求特别精准的功能,可以把定位精度降低
- 按需使用定位,不用的时候关闭它
2.网络请求
- app推到后台时,尽量减少网络请求,不是必要的请求,可以等用户切换到前台时再去请求
- app退到后台时,大部分的时候,刷新UI的操作是不必要的
其他
- 刷新UITableView或者UICollectionView的时候,尽量做到刷新一行,或者刷新一个section
- 像对UIView的圆角、阴影等效果能用图片图标做到的,一律不用代码写
- 网络请求的数据如果需要的话,尽量做本地缓存
- 蓝牙按需使用,不用的时候关闭它
- 定时器Timer的使用频次减少
- 大文件I/O操作时,可以用dispatch_io
比如:滴滴这种app,很多时候希望在1%的电量时,还能打个车,这个时候,我们要尽量把除了发布打车订单的请求保留,其他的数据请求应该暂时禁用。