原文链接:App 启动速度怎么做优化与监控?
02 章节 App 启动速度怎么做优化与监控?
此章节开篇就指出一个APP从点击启动到首页展示完毕分为三个步骤,并且就监控和优化两个维度展开,重点描述的部分是监控启动时间的部分,介绍了从Xcode自带工具Time Profiler、hook objc_msgSend和汇编层面的监控手段。能够更好的监控APP在哪个地方存在问题,就可以更快速的解决问题。
以上就是原作者的大概思路,具体的优化手段还要根据自己的项目,找寻适合自己的解决方式。
三个步骤:
1 main() 函数执行之前
2 main() 函数执行之后
3 首屏渲染完成后
1 main() 函数执行之前
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
或者:
@UIApplicationMain
这个阶段的耗时可以通过配置环境变量查看。DYLD_PRINT_STATISTICS
从上图我们可以知道,这个阶段的系统工作大致为:
1 dylib loading | 加载动态链接库。可以看到这个部分是耗时最多的,相应的优化手段应该是尽量减少动态库的加载,或者尽量将多个动态库进行合并使用。
2 rebease/binding | 进行rebase指针调整和bind符号绑定。
3 ObjC setup | 运行时初始处理,包括相关类的注册、category注册、selector唯一性检查等。这个部分的优化手段是定期清理项目中不再使用的类或者方法,因为在功能迭代时期可能会淘汰掉很多不再需要的功能,应该将这些文件及时清理。
4 initializer | 初始化,包括了执行+load()方法、创建静态全局变量等。在一个+load()方法里,进行运行时方法替换会带来4毫秒的消耗,这部分消耗积少成多,可以选择使用+initialize()方法替换,或者将这部分内容放到首屏渲染完成后执行,进行分流。还有
由于这部分主要是系统级别的操作,所以我们能优化的范围很小,主要是要了解这个阶段系统的主要工作,尽量避免使用动态库,检查+load()方法等。
2 main() 函数执行之后 和 首屏渲染完成后
首先我们先通过代码明确一下main() 函数执行之后
和首屏渲染完成后
具体是什么意思?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSLog(@"didFinishLaunchingWithOptions 开始执行");
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
ViewController *vc = [[ViewController alloc] init];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
NSLog(@"didFinishLaunchingWithOptions 执行完毕");
return YES;
}
然后我们在ViewController
的ViewDidLoad
方法 和 viewDidAppear
方法最后进行标记,运行程序,控制台结果如下:
2019-03-20 16:57:42.677767+0800 demo[18189:898053] didFinishLaunchingWithOptions 开始执行
2019-03-20 16:57:42.746386+0800 demo[18189:898053] viewDidLoad 执行完毕
2019-03-20 16:57:42.749525+0800 demo[18189:898053] didFinishLaunchingWithOptions 执行完毕
2019-03-20 17:14:09.568386+0800 demo[18329:915042] viewDidAppear 执行完毕
可以看出,
1 main() 函数执行之后
这个阶段,指的是从 AppDelegate 的 didFinishLaunchingWithOptions
方法开始,直到首页控制器的ViewDidLoad
方法执行完毕。
2 首页渲染完毕后
这个阶段,指的是从首页控制器 ViewDidLoad
方法结束,直到 AppDelegate 的 didFinishLaunchingWithOptions
方法结束。
那么这两个阶段分别又执行什么任务呢?耗时又如何监控?
3 分别做了什么任务?
在 main() 函数执行之后 到 首屏渲染完成前,主要任务包括:
1 首屏初始化所需配置文件的读写
2 首屏所需数据的读取
3 首屏渲染的计算等
在 首屏渲染完成后,主要完成:
1 非首屏业务模块的初始化
2 系统监听注册
3 配置文件的读取等
此时用户已经可以看到APP的首页了,以上这些不紧急的任务最好在这个阶段处理。
4 如何监控耗时?
想要进行耗时优化,首先的工作是监控各个任务消耗的时间。在本文开始处提到过戴铭老师的原文中介绍了3中监控手段(Xcode自带工具Time Profiler、hook objc_msgSend和汇编层面的监控),并对于后两种监控方法进行了深入的讲解,感兴趣的朋友可以跳转到原文去查看。
1 hook objc_msgSend 方法,是基于Facebook开源的 fishhook 库。
2 汇编层面的引入,参考戴铭老师的开源项目 GCDFetchFeed。
这部分的内容非常底层,我个人是没有非常理解,只是对于 fishhook 库进行了简单的测试而已。但是不要灰心,从大神的思路出发,不断学习和提高自己才是关键,不要急于求成。
那么,看不懂上面的内容,我们就没办法继续优化APP的启动时间了吗?显然不是!
理解了APP启动的流程,我们大概能感受到,启动速度的优化主要集中在didFinishLaunchingWithOptions
中,这里主要进行的工作就是:
1 初始化各种SDK
2 初始化各种工具类
3 配置各种监听等
我们需要做的就是根据项目需求,有的放矢的安排各种工作的运行顺序并解偶 AppDelegate,巧妙的使用预加载方法,优化首页的布局等。
最后
优化工作应该是贯穿始终的,不可一蹴而就。
优化工作更需要前后端的密切配合。
由于戴铭老师这篇文章内容太干,需要慢慢消化,先总结到这里。欢迎大家补充。
参考
iOS App 启动性能优化
一次立竿见影的启动时间优化
Session 406 Optimizing App Startup Time