iOS 端启动速度优化

应用启动流程

iOS 启动可以分为pre-main阶段和main()阶段

pre-main阶段

  • 加载可执行文件
  • 加载动态链接库加载器 dyld
  • 递归加载动态链接库 dylib

main 阶段

  • dyld 调用 main
  • 调用 UIApplicationMain
  • 调用 applicationWillFinishLaunching
  • 调用 didFinishLaunchingWithOptions

耗时测量

pre-main 阶段

  • Xcode 的环境变量中 DYLD_PRINT_STATICTICS = 1
  • 日志里面会有详细的内容

main 阶段

  • 测量 main -> didFinishLaunchingWithOptions 的时间

优化思路

  • dyld 加载步骤
    • Load dylibs

      分析依赖、Mach-o文件、验证有效性、代码签名注册到内核、对 dylib segment 调用 mmap

      • 避免使用 embedded dylib
      • 合并已有的 dylib , 使用静态库
      • 懒加载 dylib
    • Rebase/Bind

      Rebase 读入镜像、修正 ASLR 导致的内存地址的偏差。消耗在文件 I/O
      Bind 查询符号表、设置镜像的外部指针。消耗在 CPU 计算

      • 减少ObjCClassSelectorCategory 数量
      • 减少C++ 虚函数数量(创建虚函数表有开销)
      • 使用 Swift struct(内部做了优化、符号开销少)
    • Object setup

      dyld 会注册声明过的 ObjC 类,将分类方法插入到类的方法列表中、检查 selector 的唯一性

    • Initializers

      dyld 开始运行程序的初始化函数、调用每个 ObjC 分类+load 方法,调用 C/C++ 中的构造器函数 ( __attibute__(constructor) 修饰的函数), 创建非基本类型的 C++ 静态全局变量,随后调用 main 函数

      • 减少 +load 函数做的事情,推迟到 +initialize
      • 减少构造器函数的个数,在构造器函数里少作些事情
      • 减少 C++ 静态全局变量个数

main 阶段

减少 didFinishLaunchingWithOptions 方法里的事情

  • 梳理依赖库、延迟加载可以延迟加载的库
  • 把可以延迟执行的逻辑,放到后面。
  • 避免复杂/多余计算
  • 避免在 viewDidLoad 和 viewWillAppear中做太多的事情
  • 首页控制器用纯代码方式构建
作者

shouyi.www

发布于

2020-02-19

更新于

2025-01-30

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×