07.App启动速度优化

一:启动时间

启动时间 = main()之前的时间 + main()之后的时间

1.1 如何衡量时间

  • 通过添加环境变量可以打印出APP的启动时间分析

    • Edit scheme -> Run -> Arguments

    • DYLD_PRINT_STATISTICS 设置为 1

    • 如果需要更详细的信息,那就将 DYLD_PRINT_STATISTICS_DETAILS 设置为 1

1.2 作为开发者我们可以介入的地方有哪些?

二: main()之前

2.1 dyld

iOS的动态链接器,可以用来装载Mach-O文件(可执行文件、动态库等)

  • 启动APP时,dyld所做的事情有

    • 装载APP的可执行文件,同时会递归加载所有依赖的动态库

    • dyld把可执行文件、动态库都装载完毕后,会通知Runtime进行下一步的处理

2.2 runtime

  • 启动APP时,runtime所做的事情有

    • 调用map_images进行可执行文件(.o)内容的解析和处理

    • load_images中调用call_load_methods,调用所有ClassCategory+load方法

    • 进行各种objc结构的初始化(注册Objc类初始化类对象等等)

    • 调用C++静态初始化器__attribute__((constructor))修饰的函数

到此为止,可执行文件和动态库中所有的符号(ClassProtocolSelectorIMP,…)都已经按格式成功加载到内存中,被runtime所管理

2.3 可优化点

  • 减少不必要的framework

  • framework应当设为optionalrequired,如果该framework在当前App支持的所有iOS系统版本都存在,那么就设为required,否则就设为optional,因为optional会有些额外的检查

  • 合并或者删减一些OC类,关于清理项目中没用到的类,使用工具AppCode代码检查功能,查到当前项目中没有用到的类如

  • 删除废弃方法

  • +load里少做事情,用+initialize方法和dispatch_once取代所有的__attribute__((constructor))C++静态构造器ObjC+load

  • 尽量不用C++虚函数(创建虚函数有开销)

  • Swift尽量使用Struct

三: main()之后

主要是App本身相关的一些初始化服务

  • 不适用xib/sb,使用代码加载首页视图

  • NSUserDefaults实际上是在Library文件夹下会生产一个plist文件,如果文件太大的话一次能读取到内存中可能很耗时,这个影响需要评估,如果耗时很大的话需要拆分(需考虑老版本覆盖安装兼容问题)

  • 每次用NSLog方式打印会隐式的创建一个Calendar,因此需要删减启动时各业务方打的log,或者仅仅针对内测版输出log

  • 梳理应用启动时发送的所有网络请求,是否可以统一在异步线程请求

  • 首页相关控制器的viewDidLoad/viewWillAppear里少做操作或者延迟做

  • 少做/不做 I/O 操作,或者异步做

参考

https://techblog.toutiao.com/2017/01/17/iosspeed/

Last updated