dyld 加载性能优化:App 启动前到底做了什么

前言

当用户点击 App 图标到看到第一个界面,这短短的几百毫秒内,系统做了大量的工作。这段时间被称为 pre-main 阶段,完全由 dyld(Dynamic Loader)主导。

理解 dyld 的工作原理,是进行启动优化的基础。本文将深入剖析 dyld 的每一个步骤,并给出针对性的优化策略。


一、启动时间的构成

1.1 启动阶段划分

┌─────────────────────────────────────────────────────────────────────┐
│                    App 启动时间构成                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   用户点击图标                                                       │
│        │                                                             │
│        ▼                                                             │
│   ╔═══════════════════════════════════════════════════════════════╗ │
│   ║              T1: pre-main 阶段 (dyld 主导)                    ║ │
│   ║  ┌─────────────────────────────────────────────────────────┐  ║ │
│   ║  │  • 内核创建进程                                          │  ║ │
│   ║  │  • dyld 初始化                                          │  ║ │
│   ║  │  • 加载动态库                                            │  ║ │
│   ║  │  • Rebase & Binding                                     │  ║ │
│   ║  │  • ObjC Runtime 初始化                                  │  ║ │
│   ║  │  • +load 方法和静态构造函数                              │  ║ │
│   ║  └─────────────────────────────────────────────────────────┘  ║ │
│   ╚═══════════════════════════════════════════════════════════════╝ │
│        │                                                             │
│        ▼ main() 函数开始执行                                        │
│   ╔═══════════════════════════════════════════════════════════════╗ │
│   ║              T2: main() 阶段 (开发者主导)                     ║ │
│   ║  ┌─────────────────────────────────────────────────────────┐  ║ │
│   ║  │  • main() 执行                                          │  ║ │
│   ║  │  • UIApplicationMain()                                  │  ║ │
│   ║  │  • application:didFinishLaunchingWithOptions:           │  ║ │
│   ║  │  • 首屏 UI 构建                                         │  ║ │
│   ║  └─────────────────────────────────────────────────────────┘  ║ │
│   ╚═══════════════════════════════════════════════════════════════╝ │
│        │                                                             │
│        ▼ 首帧渲染完成                                               │
│   ╔═══════════════════════════════════════════════════════════════╗ │
│   ║              T3: 首帧渲染阶段                                  ║ │
│   ║  ┌─────────────────────────────────────────────────────────┐  ║ │
│   ║  │  • viewDidLoad                                          │  ║ │
│   ║  │  • viewWillAppear                                       │  ║ │
│   ║  │  • Layout & Render                                      │  ║ │
│   ║  │  • CA::Transaction::commit()                            │  ║ │
│   ║  └─────────────────────────────────────────────────────────┘  ║ │
│   ╚═══════════════════════════════════════════════════════════════╝ │
│        │                                                             │
│        ▼                                                             │
│   用户看到首屏内容,可以交互                                         │
│                                                                      │
│   ════════════════════════════════════════════════════════════════  │
│                                                                      │
│   启动类型:                                                         │
│   • 冷启动:App 完全不在内存中,完整走上述流程                        │
│   • 热启动:App 在后台被挂起,恢复执行                               │
│   • 温启动:App 被终止但部分缓存还在,介于两者之间                    │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

1.2 测量启动时间

// 1. 使用环境变量测量 pre-main 时间
// Xcode → Edit Scheme → Run → Arguments → Environment Variables
// DYLD_PRINT_STATISTICS = 1
// DYLD_PRINT_STATISTICS_DETAILS = 1

/* 
输出示例:

Total pre-main time: 847.25 milliseconds (100.0%)
         dylib loading time: 295.52 milliseconds (34.8%)
        rebase/binding time:  72.80 milliseconds (8.5%)
            ObjC setup time:  89.23 milliseconds (10.5%)
           initializer time: 389.15 milliseconds (45.9%)
           slowest intializers :
               libSystem.B.dylib :   8.89 milliseconds (1.0%)
              libMainThreadChecker.dylib :  33.33 milliseconds (3.9%)
                    MyFramework :  156.78 milliseconds (18.5%)
                          MyApp :  189.45 milliseconds (22.3%)
*/

// 2. 代码中测量
CFAbsoluteTime StartTime;

int main(int argc, char * argv[]) {
    StartTime = CFAbsoluteTimeGetCurrent();
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

// AppDelegate.m
- (BOOL)application:(UIApplication *)application 
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    CFAbsoluteTime launchTime = CFAbsoluteTimeGetCurrent() - StartTime;
    NSLog(@"didFinishLaunching 耗时: %.2f ms", launchTime * 1000);
    
    return YES;
}

// 首帧渲染
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        CFAbsoluteTime firstFrameTime = CFAbsoluteTimeGetCurrent() - StartTime;
        NSLog(@"首帧渲染耗时: %.2f ms", firstFrameTime * 1000);
    });
}

1.3 使用 Instruments 分析

┌─────────────────────────────────────────────────────────────────────┐
│                    Instruments 启动分析工具                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   1. App Launch                                                      │
│      Xcode → Product → Profile → App Launch                         │
│      • 完整的启动时间线                                              │
│      • 各阶段耗时统计                                                │
│      • 方法级别的时间分析                                            │
│                                                                      │
│   2. Time Profiler                                                   │
│      • 采样 CPU 使用情况                                             │
│      • 发现耗时函数                                                  │
│                                                                      │
│   3. System Trace                                                    │
│      • 系统级别追踪                                                  │
│      • 线程调度、虚拟内存等                                          │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

二、dyld 工作流程详解

2.1 dyld 演进历史

┌─────────────────────────────────────────────────────────────────────┐
│                    dyld 版本演进                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   dyld 1.0 (1996-2004)                                              │
│   └── 最初版本,基于 NeXTSTEP                                       │
│                                                                      │
│   dyld 2.0 (2004-2017)                                              │
│   └── macOS 10.4+                                                   │
│   └── 完全重写,支持预绑定                                          │
│   └── 引入共享缓存 (Shared Cache)                                   │
│                                                                      │
│   dyld 3.0 (2017-至今)                                              │
│   └── iOS 11 (部分), iOS 13+ (全面)                                │
│   └── 引入 Launch Closure 机制                                      │
│   └── 大部分工作移到 App 安装/更新时完成                            │
│   └── 显著提升启动速度                                              │
│                                                                      │
│   dyld 4.0 (2022-至今)                                              │
│   └── iOS 16+, macOS 13+                                           │
│   └── 进一步优化 Closure 机制                                       │
│   └── 支持 Swift 并发                                               │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

2.2 dyld 2 vs dyld 3

┌─────────────────────────────────────────────────────────────────────┐
│                    dyld 2 vs dyld 3                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   【dyld 2 - 每次启动时】                                            │
│                                                                      │
│   App 启动                                                           │
│       │                                                              │
│       ├── 解析 Mach-O Headers                                       │
│       ├── 查找依赖库                                                │
│       ├── 映射所有 Mach-O 文件                                      │
│       ├── 执行符号查找                                               │
│       ├── Rebase & Binding                                          │
│       ├── 运行 Initializers                                         │
│       │                                                              │
│       └── main()                                                    │
│                                                                      │
│   问题:每次启动都要重复大量工作                                     │
│                                                                      │
│   ════════════════════════════════════════════════════════════════  │
│                                                                      │
│   【dyld 3 - 分离计算和执行】                                        │
│                                                                      │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  App 安装/更新时 (Out-of-process)                            │   │
│   │                                                              │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  • 解析所有 Mach-O 文件                                 │ │   │
│   │  │  • 分析依赖关系                                         │ │   │
│   │  │  • 执行符号查找                                         │ │   │
│   │  │  • 生成 Launch Closure (缓存结果)                      │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   │                         │                                    │   │
│   │                         ▼                                    │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │        Launch Closure (二进制缓存文件)                  │ │   │
│   │  │  存储位置:/var/containers/.../Closure/                 │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                               │                                      │
│                               ▼                                      │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  App 启动时 (In-process)                                     │   │
│   │                                                              │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  • 验证 Closure 是否有效                                │ │   │
│   │  │  • 直接使用缓存的结果                                   │ │   │
│   │  │  • 映射动态库(利用共享缓存)                           │ │   │
│   │  │  • 执行 Fixups (Rebase/Bind)                           │ │   │
│   │  │  • 运行 Initializers                                   │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   │                         │                                    │   │
│   │                         ▼                                    │   │
│   │                      main()                                  │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   优势:启动时跳过大量解析和查找工作                                 │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

2.3 dyld 加载详细步骤

┌─────────────────────────────────────────────────────────────────────┐
│                    dyld 加载详细流程                                 │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Step 1: 内核准备 (exec syscall)                             │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  1. fork() 创建新进程                                   │ │   │
│   │  │  2. 解析 Mach-O header,验证合法性                      │ │   │
│   │  │  3. 加载 App 二进制到虚拟内存                           │ │   │
│   │  │  4. 加载 dyld 到进程地址空间                            │ │   │
│   │  │  5. 设置进程状态,跳转到 dyld 入口 (_dyld_start)        │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                       │
│                              ▼                                       │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Step 2: dyld 初始化                                         │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  1. 设置运行环境(环境变量、参数)                      │ │   │
│   │  │  2. 初始化 dyld 内部数据结构                            │ │   │
│   │  │  3. 检查 Launch Closure 是否有效                        │ │   │
│   │  │     • 有效:使用缓存的 Closure                          │ │   │
│   │  │     • 无效:回退到 dyld2 模式,实时计算                 │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                       │
│                              ▼                                       │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Step 3: 加载共享缓存 (Shared Cache)                         │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  共享缓存位置:                                          │ │   │
│   │  │  /System/Library/Caches/com.apple.dyld/                 │ │   │
│   │  │                                                         │ │   │
│   │  │  内容:                                                  │ │   │
│   │  │  • 所有系统库的合并优化版本                             │ │   │
│   │  │  • 预先完成了符号绑定                                   │ │   │
│   │  │  • 所有进程共享,节省内存                               │ │   │
│   │  │                                                         │ │   │
│   │  │  优势:                                                  │ │   │
│   │  │  • 系统库无需单独加载                                   │ │   │
│   │  │  • 符号查找极快                                         │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                       │
│                              ▼                                       │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Step 4: 实例化主程序                                        │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  1. 读取主程序的 Load Commands                          │ │   │
│   │  │  2. 映射 __TEXT, __DATA 等 segments                    │ │   │
│   │  │  3. 应用 ASLR slide                                    │ │   │
│   │  │  4. 创建主程序的 ImageLoader 对象                       │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                       │
│                              ▼                                       │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Step 5: 加载动态库                                          │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  遍历 LC_LOAD_DYLIB 命令:                               │ │   │
│   │  │                                                         │ │   │
│   │  │  for each dylib in dependencies:                       │ │   │
│   │  │      1. 检查是否在共享缓存中 → 直接使用                 │ │   │
│   │  │      2. 否则从文件系统加载                              │ │   │
│   │  │      3. 递归加载该库的依赖                              │ │   │
│   │  │      4. 创建 ImageLoader 对象                           │ │   │
│   │  │                                                         │ │   │
│   │  │  加载顺序:广度优先,按依赖层级                         │ │   │
│   │  │                                                         │ │   │
│   │  │  ⚠️ 这一步是耗时大户!动态库越多,越慢                  │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                       │
│                              ▼                                       │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Step 6: Rebase                                              │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  ASLR 导致加载地址随机化,需要修正内部指针              │ │   │
│   │  │                                                         │ │   │
│   │  │  编译时地址: 0x100001234                                │ │   │
│   │  │  ASLR slide: 0x000050000                                │ │   │
│   │  │  运行时地址: 0x100051234                                │ │   │
│   │  │                                                         │ │   │
│   │  │  修正操作:遍历 __DATA 中的指针,加上 slide             │ │   │
│   │  │                                                         │ │   │
│   │  │  影响因素:                                              │ │   │
│   │  │  • ObjC 类越多,指针越多                                │ │   │
│   │  │  • Swift 结构体比类更好(无需 rebase)                  │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                       │
│                              ▼                                       │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Step 7: Binding                                             │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  绑定外部符号(从其他动态库导入)                       │ │   │
│   │  │                                                         │ │   │
│   │  │  Non-Lazy Binding (启动时):                             │ │   │
│   │  │  • 全局变量引用                                         │ │   │
│   │  │  • __got 表中的符号                                    │ │   │
│   │  │                                                         │ │   │
│   │  │  Lazy Binding (首次调用时):                             │ │   │
│   │  │  • 函数引用                                             │ │   │
│   │  │  • 通过 __stubs → __stub_helper → dyld_stub_binder     │ │   │
│   │  │                                                         │ │   │
│   │  │  Weak Binding:                                          │ │   │
│   │  │  • 可选依赖,符号可能不存在                             │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                       │
│                              ▼                                       │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Step 8: ObjC Runtime Setup                                  │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  由 libobjc.dylib 中的 _objc_init 触发:                │ │   │
│   │  │                                                         │ │   │
│   │  │  1. 注册所有 ObjC 类                                    │ │   │
│   │  │     • 读取 __objc_classlist                            │ │   │
│   │  │     • 调用 objc_readClassPair()                        │ │   │
│   │  │     • 建立类的继承关系                                  │ │   │
│   │  │                                                         │ │   │
│   │  │  2. 注册 Category                                       │ │   │
│   │  │     • 读取 __objc_catlist                              │ │   │
│   │  │     • 将 Category 方法附加到类上                       │ │   │
│   │  │                                                         │ │   │
│   │  │  3. 确保 Selector 唯一性                                │ │   │
│   │  │     • 合并相同 selector                                │ │   │
│   │  │                                                         │ │   │
│   │  │  ⚠️ 类数量直接影响这一步的耗时                          │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                       │
│                              ▼                                       │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Step 9: Initializers                                        │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  按依赖顺序执行初始化(被依赖的先执行):               │ │   │
│   │  │                                                         │ │   │
│   │  │  1. libSystem.B.dylib 初始化                           │ │   │
│   │  │     • dispatch_init()                                  │ │   │
│   │  │     • libdispatch_init()                              │ │   │
│   │  │     • os_log_init()                                   │ │   │
│   │  │                                                         │ │   │
│   │  │  2. libobjc.A.dylib 初始化                             │ │   │
│   │  │     • _objc_init()                                    │ │   │
│   │  │     • 注册 dyld 回调                                   │ │   │
│   │  │                                                         │ │   │
│   │  │  3. 其他动态库的初始化                                  │ │   │
│   │  │                                                         │ │   │
│   │  │  4. 主程序初始化                                        │ │   │
│   │  │     • C++ 静态构造函数 (__mod_init_func)               │ │   │
│   │  │     • ObjC +load 方法                                  │ │   │
│   │  │     • __attribute__((constructor)) 函数                │ │   │
│   │  │                                                         │ │   │
│   │  │  ⚠️ 这是开发者最能控制的阶段!                          │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                              │                                       │
│                              ▼                                       │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Step 10: 跳转到 main()                                      │   │
│   │  ┌────────────────────────────────────────────────────────┐ │   │
│   │  │  从 LC_MAIN 获取入口点偏移                              │ │   │
│   │  │  计算真实地址 = 基址 + 偏移 + ASLR slide               │ │   │
│   │  │  跳转执行 main() 函数                                   │ │   │
│   │  │                                                         │ │   │
│   │  │  ✅ pre-main 阶段结束                                   │ │   │
│   │  └────────────────────────────────────────────────────────┘ │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

三、各阶段耗时分析

3.1 获取详细统计

// 环境变量设置
/*
DYLD_PRINT_STATISTICS = 1
DYLD_PRINT_STATISTICS_DETAILS = 1

详细输出:
  total time: 847.25 milliseconds (100.0%)
  total images loaded:  387 (380 from dyld shared cache)
  total segments mapped: 23, into 1847 pages
  total images loading time: 295.52 milliseconds (34.8%)
  total load time in ObjC:  89.23 milliseconds (10.5%)
  total debugger pause time:   0.00 milliseconds (0.0%)
  total dtrace DOF registration time:   0.00 milliseconds (0.0%)
  total rebase fixups:  234,567
  total rebase fixups time:  42.36 milliseconds (5.0%)
  total binding fixups: 567,890
  total binding fixups time:  30.44 milliseconds (3.5%)
  total weak binding fixups time:   0.78 milliseconds (0.0%)
  total redo coalesced images time:   5.67 milliseconds (0.6%)
  total symbol trie searches:    890,123
  total symbol table binary searches:    12,345
  total images initializer time: 389.15 milliseconds (45.9%)
  total images with lazy symbol bindings:   7
  total symbol trie searches during lazy bind:   234
*/

3.2 各阶段优化重点

┌─────────────────────────────────────────────────────────────────────┐
│                    各阶段优化重点                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   阶段                │  典型占比   │  优化方向                       │
│   ───────────────────┼────────────┼─────────────────────────────    │
│   dylib loading      │  30-40%    │  减少动态库数量                  │
│   rebase/binding     │  10-15%    │  减少 ObjC 类/指针数量           │
│   ObjC setup         │  10-15%    │  减少类/分类/协议数量            │
│   initializers       │  40-50%    │  延迟 +load,减少构造函数         │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

四、动态库加载优化

4.1 动态库加载过程

// 查看当前加载的所有动态库
#import <mach-o/dyld.h>

void printLoadedDylibs(void) {
    uint32_t count = _dyld_image_count();
    NSLog(@"已加载 %u 个动态库:", count);
    
    uint32_t fromCache = 0;
    uint32_t fromFile = 0;
    
    for (uint32_t i = 0; i < count; i++) {
        const char *name = _dyld_get_image_name(i);
        intptr_t slide = _dyld_get_image_vmaddr_slide(i);
        
        // 判断是否来自共享缓存
        // 共享缓存中的库 slide 通常是负值或特定模式
        NSString *imageName = [NSString stringWithUTF8String:name];
        
        if ([imageName hasPrefix:@"/System"] || 
            [imageName hasPrefix:@"/usr/lib"]) {
            fromCache++;
        } else {
            fromFile++;
            NSLog(@"  [文件] %s (slide: 0x%lx)", name, slide);
        }
    }
    
    NSLog(@"共享缓存: %u, 文件加载: %u", fromCache, fromFile);
}

4.2 减少动态库数量

┌─────────────────────────────────────────────────────────────────────┐
│                    动态库优化策略                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   【策略 1: 合并动态库】                                             │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Before:                                                     │   │
│   │  ├── NetworkKit.framework                                   │   │
│   │  ├── StorageKit.framework                                   │   │
│   │  ├── UIHelper.framework                                     │   │
│   │  └── Utils.framework                                        │   │
│   │                                                              │   │
│   │  After:                                                      │   │
│   │  └── CommonKit.framework (合并上述所有)                      │   │
│   │                                                              │   │
│   │  注意:合并后单个库太大可能影响增量编译速度                   │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【策略 2: 转为静态库】                                             │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  动态库 (.framework, .dylib):                               │   │
│   │  • 运行时加载                                                │   │
│   │  • 每个库都需要 mmap、rebase、bind                          │   │
│   │                                                              │   │
│   │  静态库 (.a, static .framework):                            │   │
│   │  • 编译时链接进主程序                                        │   │
│   │  • 无额外加载开销                                            │   │
│   │  • 但会增加主程序大小                                        │   │
│   │                                                              │   │
│   │  CocoaPods:                                                  │   │
│   │  use_frameworks! :linkage => :static                        │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【策略 3: 延迟加载】                                               │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  使用 dlopen 按需加载:                                      │   │
│   │                                                              │   │
│   │  // 首屏不需要的功能库                                       │   │
│   │  - (void)loadShareFramework {                               │   │
│   │      void *handle = dlopen("ShareKit.framework/ShareKit",   │   │
│   │                            RTLD_LAZY);                       │   │
│   │      if (handle) {                                           │   │
│   │          // 使用动态获取的符号                               │   │
│   │      }                                                       │   │
│   │  }                                                           │   │
│   │                                                              │   │
│   │  或使用 LC_LAZY_LOAD_DYLIB:                                 │   │
│   │  Other Linker Flags: -lazy_framework ShareKit               │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【策略 4: 检查不必要的依赖】                                       │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  # 查看依赖的动态库                                          │   │
│   │  otool -L MyApp                                              │   │
│   │                                                              │   │
│   │  # 检查是否有未使用的动态库                                  │   │
│   │  # 使用 Xcode → Build Settings → Dead Code Stripping        │   │
│   │                                                              │   │
│   │  常见问题:                                                  │   │
│   │  • Pod 引入了过多依赖                                        │   │
│   │  • Debug 工具库混入 Release                                  │   │
│   │  • 过期功能的库未移除                                        │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

4.3 动态库加载监控

#import <mach-o/dyld.h>
#import <dlfcn.h>

// 监控动态库加载
static CFAbsoluteTime sAppStartTime;

void dylibLoadCallback(const struct mach_header *mh, intptr_t slide) {
    Dl_info info;
    if (dladdr(mh, &info) && info.dli_fname) {
        CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
        CFAbsoluteTime elapsed = (now - sAppStartTime) * 1000;
        
        NSString *name = [[NSString stringWithUTF8String:info.dli_fname] lastPathComponent];
        
        // 只关注非系统库
        if (![name hasPrefix:@"lib"] && ![name hasPrefix:@"libsystem"]) {
            NSLog(@"[%.2fms] 加载: %@", elapsed, name);
        }
    }
}

__attribute__((constructor))
static void setupDylibMonitor(void) {
    sAppStartTime = CFAbsoluteTimeGetCurrent();
    _dyld_register_func_for_add_image(dylibLoadCallback);
}

五、Rebase & Binding 优化

5.1 理解 Fixups

┌─────────────────────────────────────────────────────────────────────┐
│                    Rebase & Binding 详解                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   【Rebase - 修正内部指针】                                          │
│                                                                      │
│   代码中的内部指针示例:                                             │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  @interface MyClass : NSObject                              │   │
│   │  @property (nonatomic, strong) NSString *name;              │   │
│   │  @end                                                        │   │
│   │                                                              │   │
│   │  编译后 __DATA 中会有指针指向:                              │   │
│   │  • MyClass 的父类 (NSObject)                                │   │
│   │  • name 属性的 getter/setter 方法                           │   │
│   │  • 类的方法列表                                              │   │
│   │  • ...                                                       │   │
│   │                                                              │   │
│   │  这些指针都需要加上 ASLR slide                               │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   Rebase 信息格式(ULEB128 编码):                                  │
│   ┌───────────────────────────────────────────────────────────────┐ │
│   │  Opcode          │  操作                                      │ │
│   │  ─────────────────────────────────────────────────────────── │ │
│   │  REBASE_OPCODE_SET_TYPE_IMM          │ 设置指针类型          │ │
│   │  REBASE_OPCODE_SET_SEGMENT_AND_OFFSET│ 设置段和偏移          │ │
│   │  REBASE_OPCODE_ADD_ADDR_ULEB         │ 移动地址              │ │
│   │  REBASE_OPCODE_DO_REBASE_IMM_TIMES   │ 执行 N 次 rebase     │ │
│   │  REBASE_OPCODE_DONE                  │ 结束                  │ │
│   └───────────────────────────────────────────────────────────────┘ │
│                                                                      │
│   ════════════════════════════════════════════════════════════════  │
│                                                                      │
│   【Binding - 绑定外部符号】                                         │
│                                                                      │
│   代码中的外部引用示例:                                             │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  #import <Foundation/Foundation.h>                          │   │
│   │                                                              │   │
│   │  NSLog(@"Hello");  // 引用 Foundation 的 NSLog              │   │
│   │  // 需要绑定到 Foundation.framework 中的实际地址            │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   Binding 类型:                                                     │
│   ┌───────────────────────────────────────────────────────────────┐ │
│   │  类型            │  时机         │  存储位置                   │ │
│   │  ─────────────────────────────────────────────────────────── │ │
│   │  Non-Lazy Bind  │  启动时       │  __got                     │ │
│   │  Lazy Bind      │  首次调用时   │  __la_symbol_ptr           │ │
│   │  Weak Bind      │  启动时       │  可被覆盖的符号            │ │
│   └───────────────────────────────────────────────────────────────┘ │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

5.2 查看 Fixups 数量

# 使用 dyldinfo 查看
$ dyldinfo -rebase -bind MyApp | wc -l

# 详细信息
$ dyldinfo -rebase MyApp
rebase information (from compressed dyld info):
segment section          address     type
__DATA  __objc_classlist 0x100008000 pointer
__DATA  __objc_classlist 0x100008008 pointer
...

$ dyldinfo -bind MyApp
bind information:
segment section          address        type    addend dylib            symbol
__DATA  __got            0x100009000    pointer 0      libSystem        _free
__DATA  __got            0x100009008    pointer 0      libobjc          _objc_msgSend
...

5.3 减少 Fixups 的策略

┌─────────────────────────────────────────────────────────────────────┐
│                    减少 Fixups 的策略                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   【1. 减少 ObjC 类数量】                                            │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  每个 ObjC 类都会产生大量指针:                              │   │
│   │  • isa 指针                                                  │   │
│   │  • 父类指针                                                  │   │
│   │  • 方法列表指针                                              │   │
│   │  • 属性列表指针                                              │   │
│   │  • ivar 布局指针                                            │   │
│   │  • ...                                                       │   │
│   │                                                              │   │
│   │  优化方案:                                                  │   │
│   │  • 合并功能相近的小类                                        │   │
│   │  • 使用组合代替继承                                          │   │
│   │  • Swift 结构体代替类(值类型无需 rebase)                   │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【2. 减少 C++ 虚函数】                                             │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  每个有虚函数的类都有虚函数表(vtable)                      │   │
│   │  vtable 中的每个函数指针都需要 rebase                       │   │
│   │                                                              │   │
│   │  优化方案:                                                  │   │
│   │  • 使用 final 关键字阻止继承                                 │   │
│   │  • 优先使用非虚函数                                          │   │
│   │  • 考虑 CRTP 等编译期多态                                    │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【3. 使用 Swift 优化】                                             │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Swift 值类型:                                              │   │
│   │  struct Point {                                              │   │
│   │      var x: Double                                           │   │
│   │      var y: Double                                           │   │
│   │  }                                                           │   │
│   │  // 不产生 rebase/bind                                      │   │
│   │                                                              │   │
│   │  Swift 类(final):                                         │   │
│   │  final class Manager {                                       │   │
│   │      // 比 ObjC 类产生更少的指针                            │   │
│   │  }                                                           │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【4. 减少全局/静态变量】                                           │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  // Bad: 全局对象指针                                        │   │
│   │  static NSArray *kItems = @[@"a", @"b", @"c"];              │   │
│   │                                                              │   │
│   │  // Good: 懒加载或方法内局部变量                             │   │
│   │  + (NSArray *)items {                                        │   │
│   │      static NSArray *items;                                  │   │
│   │      static dispatch_once_t onceToken;                       │   │
│   │      dispatch_once(&onceToken, ^{                            │   │
│   │          items = @[@"a", @"b", @"c"];                        │   │
│   │      });                                                     │   │
│   │      return items;                                           │   │
│   │  }                                                           │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

六、ObjC 运行时初始化优化

6.1 ObjC Setup 过程

// libobjc 中的初始化流程
void _objc_init(void) {
    // 1. 初始化运行时环境
    environ_init();
    tls_init();
    static_init();
    runtime_init();
    exception_init();
    
    // 2. 注册 dyld 回调
    _dyld_objc_notify_register(
        &map_images,        // 镜像加载时
        &load_images,       // 镜像初始化时
        &unmap_image        // 镜像卸载时
    );
}

// map_images - 处理类注册
void map_images(unsigned count, const char * const paths[],
                const struct mach_header * const mhdrs[]) {
    // 遍历所有镜像
    for (uint32_t i = 0; i < count; i++) {
        // 读取 ObjC 元数据
        _read_images(mhdrs[i], paths[i]);
    }
}

// _read_images 内部会:
// 1. 读取 __objc_classlist,注册所有类
// 2. 读取 __objc_catlist,附加 Category
// 3. 读取 __objc_protolist,注册协议
// 4. 读取 __objc_selrefs,唯一化 selector

6.2 查看 ObjC 元数据量

# 查看 ObjC 相关 section 大小
$ size -m MyApp | grep objc
Section __objc_classlist: 4096
Section __objc_catlist: 512
Section __objc_protolist: 256
Section __objc_classrefs: 2048
Section __objc_selrefs: 8192
Section __objc_methname: 65536
Section __objc_methtype: 32768

# 统计类数量
$ otool -s __DATA __objc_classlist MyApp | tail -n +3 | wc -l

6.3 减少 ObjC 元数据

┌─────────────────────────────────────────────────────────────────────┐
│                    减少 ObjC 元数据                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   【1. 减少类数量】                                                  │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  分析工具:                                                  │   │
│   │  • class-dump 导出所有类                                    │   │
│   │  • LinkMap 分析各类大小                                     │   │
│   │                                                              │   │
│   │  常见优化:                                                  │   │
│   │  • 删除未使用的类                                           │   │
│   │  • 合并过度拆分的小类                                        │   │
│   │  • 使用 Swift struct 代替轻量级 ObjC 类                     │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【2. 减少 Category】                                               │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Category 问题:                                             │   │
│   │  • 每个 Category 都需要在启动时附加到原类                   │   │
│   │  • 会增加 __objc_catlist 和方法列表                         │   │
│   │                                                              │   │
│   │  优化方案:                                                  │   │
│   │  • 合并同一个类的多个 Category                              │   │
│   │  • 将工具方法改为 C 函数或 Swift 扩展                       │   │
│   │                                                              │   │
│   │  // 优化前:多个 Category                                    │   │
│   │  @interface NSString (Format) ... @end                       │   │
│   │  @interface NSString (Validation) ... @end                   │   │
│   │  @interface NSString (Crypto) ... @end                       │   │
│   │                                                              │   │
│   │  // 优化后:合并为一个                                       │   │
│   │  @interface NSString (Utils)                                 │   │
│   │  // 包含 Format、Validation、Crypto 的所有方法              │   │
│   │  @end                                                        │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【3. 优化方法签名】                                                │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  __objc_methname 存储方法名                                  │   │
│   │  __objc_methtype 存储方法类型签名                           │   │
│   │                                                              │   │
│   │  方法名过长会增加 __objc_methname 大小:                     │   │
│   │  // Bad                                                      │   │
│   │  - (void)updateUserInterfaceWithAnimatedTransitionStyle...  │   │
│   │                                                              │   │
│   │  // Good                                                     │   │
│   │  - (void)updateUI:(UIStyle)style animated:(BOOL)animated;   │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【4. 使用 Swift 替代】                                             │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Swift 类型的优势:                                          │   │
│   │                                                              │   │
│   │  • struct/enum: 无 ObjC 运行时开销                          │   │
│   │  • final class: 编译器优化,减少虚函数表                    │   │
│   │  • @objc 最小化: 只在必要时暴露给 ObjC                      │   │
│   │                                                              │   │
│   │  // Swift 结构体,零运行时开销                               │   │
│   │  struct User {                                               │   │
│   │      var name: String                                        │   │
│   │      var age: Int                                            │   │
│   │  }                                                           │   │
│   │                                                              │   │
│   │  // 需要 ObjC 互操作时才用 @objc                             │   │
│   │  @objc final class UserManager: NSObject {                   │   │
│   │      @objc func getUser() -> User { ... }                   │   │
│   │  }                                                           │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

七、Initializers 优化

7.1 初始化器的类型

┌─────────────────────────────────────────────────────────────────────┐
│                    初始化器类型                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   【执行顺序】                                                       │
│                                                                      │
│   依赖库初始化                                                       │
│        │                                                             │
│        ▼                                                             │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  1. C++ 静态构造函数 (__mod_init_func)                       │   │
│   │     • 全局对象的构造函数                                     │   │
│   │     • __attribute__((constructor)) 函数                     │   │
│   └─────────────────────────────────────────────────────────────┘   │
│        │                                                             │
│        ▼                                                             │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  2. ObjC +load 方法                                          │   │
│   │     • 所有类的 +load(按编译顺序)                           │   │
│   │     • 所有 Category 的 +load(按编译顺序)                   │   │
│   └─────────────────────────────────────────────────────────────┘   │
│        │                                                             │
│        ▼                                                             │
│   main() 函数                                                        │
│                                                                      │
│   ════════════════════════════════════════════════════════════════  │
│                                                                      │
│   【各类型详解】                                                     │
│                                                                      │
│   1. __attribute__((constructor))                                   │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  __attribute__((constructor))                                │   │
│   │  void myInit(void) {                                         │   │
│   │      NSLog(@"constructor called");                           │   │
│   │  }                                                           │   │
│   │                                                              │   │
│   │  // 可以指定优先级(数字越小越先执行,101起)                 │   │
│   │  __attribute__((constructor(101)))                           │   │
│   │  void earlyInit(void) { ... }                                │   │
│   │                                                              │   │
│   │  __attribute__((constructor(200)))                           │   │
│   │  void lateInit(void) { ... }                                 │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   2. C++ 全局对象构造                                                │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  // 全局对象,构造函数在 main 前执行                         │   │
│   │  class Logger {                                              │   │
│   │  public:                                                     │   │
│   │      Logger() { /* 初始化 */ }                               │   │
│   │  };                                                          │   │
│   │  static Logger globalLogger;  // main 前构造                │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   3. ObjC +load                                                      │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  @implementation MyClass                                     │   │
│   │  + (void)load {                                              │   │
│   │      // 在所有类加载后、main 前调用                          │   │
│   │      // 每个类和 Category 的 +load 都会被调用                │   │
│   │  }                                                           │   │
│   │  @end                                                        │   │
│   │                                                              │   │
│   │  特点:                                                      │   │
│   │  • 不像普通方法那样被继承                                    │   │
│   │  • 自动调用,无需手动触发                                    │   │
│   │  • 主线程执行,阻塞启动                                      │   │
│   │  • 运行时环境已就绪(可以使用 ObjC 方法)                    │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

7.2 +load 方法的问题

// 查找所有 +load 方法
#import <objc/runtime.h>

void findLoadMethods(void) {
    unsigned int classCount;
    Class *classes = objc_copyClassList(&classCount);
    
    NSMutableArray *classesWithLoad = [NSMutableArray array];
    
    for (unsigned int i = 0; i < classCount; i++) {
        Class cls = classes[i];
        
        // 检查类是否有 +load 方法(不包括继承的)
        unsigned int methodCount;
        Method *methods = class_copyMethodList(object_getClass(cls), &methodCount);
        
        for (unsigned int j = 0; j < methodCount; j++) {
            SEL sel = method_getName(methods[j]);
            if (sel_isEqual(sel, @selector(load))) {
                [classesWithLoad addObject:NSStringFromClass(cls)];
                break;
            }
        }
        
        free(methods);
    }
    
    free(classes);
    
    NSLog(@"有 +load 方法的类 (%lu):\n%@", 
          (unsigned long)classesWithLoad.count,
          [classesWithLoad componentsJoinedByString:@"\n"]);
}

7.3 +load 优化策略

┌─────────────────────────────────────────────────────────────────────┐
│                    +load 优化策略                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   【策略 1: 迁移到 +initialize】                                     │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  // Before: +load                                            │   │
│   │  + (void)load {                                              │   │
│   │      [self setup];                                           │   │
│   │  }                                                           │   │
│   │                                                              │   │
│   │  // After: +initialize (懒加载)                              │   │
│   │  + (void)initialize {                                        │   │
│   │      if (self == [MyClass class]) {                          │   │
│   │          [self setup];                                       │   │
│   │      }                                                       │   │
│   │  }                                                           │   │
│   │                                                              │   │
│   │  区别:                                                      │   │
│   │  • +load: 类加载时立即调用                                   │   │
│   │  • +initialize: 第一次收到消息时才调用                       │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【策略 2: 使用 dispatch_once 延迟】                                │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  // 需要某些功能时才初始化                                   │   │
│   │  + (instancetype)sharedInstance {                            │   │
│   │      static MyClass *instance;                               │   │
│   │      static dispatch_once_t onceToken;                       │   │
│   │      dispatch_once(&onceToken, ^{                            │   │
│   │          instance = [[self alloc] init];                     │   │
│   │          [instance setup];  // 延迟到首次使用                │   │
│   │      });                                                     │   │
│   │      return instance;                                        │   │
│   │  }                                                           │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【策略 3: Method Swizzling 替代方案】                              │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  // 传统:在 +load 中 swizzle                                │   │
│   │  + (void)load {                                              │   │
│   │      Method original = class_getInstanceMethod(...);         │   │
│   │      Method swizzled = class_getInstanceMethod(...);         │   │
│   │      method_exchangeImplementations(original, swizzled);     │   │
│   │  }                                                           │   │
│   │                                                              │   │
│   │  // 替代方案 1: 在 AppDelegate 中集中处理                    │   │
│   │  - (BOOL)application:didFinishLaunchingWithOptions: {        │   │
│   │      [self performSwizzling];                                │   │
│   │      return YES;                                             │   │
│   │  }                                                           │   │
│   │                                                              │   │
│   │  // 替代方案 2: 使用 Aspects 等库(首次调用时 hook)          │   │
│   │  [UIViewController aspect_hookSelector:@selector(viewDidLoad)│   │
│   │                            withOptions:AspectPositionBefore  │   │
│   │                             usingBlock:^{ ... }];            │   │
│   │                                                              │   │
│   │  // 替代方案 3: 使用代理/组合代替继承                        │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【策略 4: 审查第三方库的 +load】                                   │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  常见问题库:                                                │   │
│   │  • 老旧的统计/监控 SDK                                       │   │
│   │  • 热修复框架                                                │   │
│   │  • AOP 框架                                                  │   │
│   │                                                              │   │
│   │  解决方案:                                                  │   │
│   │  • 更新到新版本                                              │   │
│   │  • 寻找替代库                                                │   │
│   │  • 联系维护者优化                                            │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

7.4 监控 +load 耗时

// 使用 DYLD_PRINT_INITIALIZERS 环境变量
// 或者自己 hook

#import <objc/runtime.h>
#import <mach/mach_time.h>

// 注意:这需要在非常早的时机注入
// 实际项目中可以用更完善的方案

static CFMutableDictionaryRef sLoadTimes;
static mach_timebase_info_data_t sTimebaseInfo;

// Swizzle +load 的实现(概念演示,实际操作复杂)
void monitorLoadMethods(void) {
    sLoadTimes = CFDictionaryCreateMutable(NULL, 0, 
                                           &kCFTypeDictionaryKeyCallBacks,
                                           &kCFTypeDictionaryValueCallBacks);
    mach_timebase_info(&sTimebaseInfo);
    
    // 遍历所有类,记录 +load 调用时间
    // 实际实现需要在 _objc_init 之前 hook
}

void printLoadTimesReport(void) {
    NSLog(@"=== +load 方法耗时报告 ===");
    
    // 按耗时排序输出
    CFIndex count = CFDictionaryGetCount(sLoadTimes);
    if (count == 0) return;
    
    CFTypeRef *keys = malloc(sizeof(CFTypeRef) * count);
    CFTypeRef *values = malloc(sizeof(CFTypeRef) * count);
    CFDictionaryGetKeysAndValues(sLoadTimes, keys, values);
    
    // 排序并输出...
    
    free(keys);
    free(values);
}

八、实战优化案例

8.1 优化前后对比

┌─────────────────────────────────────────────────────────────────────┐
│                    启动优化案例                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   【优化前】pre-main: 1247ms                                        │
│                                                                      │
│   Total pre-main time: 1247.32 milliseconds (100.0%)                │
│            dylib loading time: 456.78 milliseconds (36.6%)          │
│           rebase/binding time: 123.45 milliseconds (9.8%)           │
│               ObjC setup time: 167.89 milliseconds (13.4%)          │
│              initializer time: 498.20 milliseconds (39.9%)          │
│              slowest intializers :                                   │
│                  SDWebImage :  89.12 milliseconds (7.1%)            │
│                 AFNetworking :  67.34 milliseconds (5.3%)           │
│                    Realm :  123.45 milliseconds (9.8%)              │
│                    MyApp : 189.56 milliseconds (15.1%)              │
│                                                                      │
│   问题分析:                                                         │
│   1. 动态库过多(87个自定义 framework)                             │
│   2. ObjC 类过多(2341个)                                          │
│   3. +load 方法过多(156个)                                        │
│   4. 第三方库初始化耗时                                              │
│                                                                      │
│   ════════════════════════════════════════════════════════════════  │
│                                                                      │
│   【优化措施】                                                       │
│                                                                      │
│   1. 动态库优化:                                                    │
│      • 合并业务 Framework: 87 → 12                                  │
│      • 小型工具库改为静态链接                                        │
│      • 非首屏功能库延迟加载                                          │
│                                                                      │
│   2. 类数量优化:                                                    │
│      • 删除无用类: -234                                             │
│      • 合并小类: -156                                               │
│      • Swift struct 替代: -89                                       │
│      • 最终: 2341 → 1862                                            │
│                                                                      │
│   3. +load 优化:                                                    │
│      • 迁移到 +initialize: 89个                                     │
│      • 延迟到 didFinishLaunching: 45个                              │
│      • 删除无用的: 12个                                             │
│      • 最终: 156 → 10                                               │
│                                                                      │
│   4. 第三方库优化:                                                  │
│      • SDWebImage: 延迟初始化图片缓存                               │
│      • Realm: 延迟到首次数据库操作                                  │
│      • 更新 AFNetworking 到最新版                                   │
│                                                                      │
│   ════════════════════════════════════════════════════════════════  │
│                                                                      │
│   【优化后】pre-main: 523ms (减少 58%)                              │
│                                                                      │
│   Total pre-main time: 523.67 milliseconds (100.0%)                 │
│            dylib loading time: 178.34 milliseconds (34.0%)          │
│           rebase/binding time:  78.12 milliseconds (14.9%)          │
│               ObjC setup time:  98.45 milliseconds (18.7%)          │
│              initializer time: 168.76 milliseconds (32.2%)          │
│              slowest intializers :                                   │
│                     libSystem.B.dylib :   8.90 milliseconds         │
│                     MyApp : 134.56 milliseconds                      │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

8.2 完整的启动优化检查清单

// StartupOptimizer.h
@interface StartupOptimizer : NSObject

// 分析当前状态
+ (void)analyzeStartupPerformance;

// 各项检查
+ (NSArray<NSString *> *)checkDylibCount;
+ (NSArray<NSString *> *)checkObjCClassCount;
+ (NSArray<NSString *> *)checkLoadMethods;
+ (NSArray<NSString *> *)checkLargeInitializers;

@end

#import "StartupOptimizer.h"
#import <mach-o/dyld.h>
#import <objc/runtime.h>
#import <dlfcn.h>

@implementation StartupOptimizer

+ (void)analyzeStartupPerformance {
    NSLog(@"========== 启动性能分析 ==========");
    
    // 1. 动态库数量
    NSArray *dylibIssues = [self checkDylibCount];
    NSLog(@"\n【动态库分析】");
    for (NSString *issue in dylibIssues) {
        NSLog(@"  %@", issue);
    }
    
    // 2. ObjC 类数量
    NSArray *classIssues = [self checkObjCClassCount];
    NSLog(@"\n【ObjC 类分析】");
    for (NSString *issue in classIssues) {
        NSLog(@"  %@", issue);
    }
    
    // 3. +load 方法
    NSArray *loadIssues = [self checkLoadMethods];
    NSLog(@"\n【+load 方法分析】");
    for (NSString *issue in loadIssues) {
        NSLog(@"  %@", issue);
    }
    
    NSLog(@"\n===================================");
}

#pragma mark - 动态库检查

+ (NSArray<NSString *> *)checkDylibCount {
    NSMutableArray *results = [NSMutableArray array];
    
    uint32_t count = _dyld_image_count();
    uint32_t systemCount = 0;
    uint32_t customCount = 0;
    NSMutableArray *customDylibs = [NSMutableArray array];
    
    for (uint32_t i = 0; i < count; i++) {
        const char *name = _dyld_get_image_name(i);
        NSString *imageName = [NSString stringWithUTF8String:name];
        
        // 判断是否是系统库
        if ([imageName hasPrefix:@"/System"] ||
            [imageName hasPrefix:@"/usr/lib"] ||
            [imageName hasPrefix:@"/Library/Apple"]) {
            systemCount++;
        } else {
            customCount++;
            NSString *shortName = [imageName lastPathComponent];
            [customDylibs addObject:shortName];
        }
    }
    
    [results addObject:[NSString stringWithFormat:@"总计: %u 个动态库", count]];
    [results addObject:[NSString stringWithFormat:@"系统库: %u 个 (共享缓存)", systemCount]];
    [results addObject:[NSString stringWithFormat:@"自定义库: %u 个", customCount]];
    
    // 警告阈值
    if (customCount > 6) {
        [results addObject:[NSString stringWithFormat:
            @"⚠️ 警告: 自定义动态库超过 6 个,建议合并或转为静态库"]];
    }
    
    // 列出所有自定义库
    if (customDylibs.count > 0) {
        [results addObject:@"自定义动态库列表:"];
        for (NSString *name in customDylibs) {
            [results addObject:[NSString stringWithFormat:@"  • %@", name]];
        }
    }
    
    return results;
}

#pragma mark - ObjC 类检查

+ (NSArray<NSString *> *)checkObjCClassCount {
    NSMutableArray *results = [NSMutableArray array];
    
    unsigned int classCount = 0;
    Class *classes = objc_copyClassList(&classCount);
    
    // 按来源分类
    NSMutableDictionary<NSString *, NSMutableArray *> *classesByImage = [NSMutableDictionary dictionary];
    
    for (unsigned int i = 0; i < classCount; i++) {
        Class cls = classes[i];
        
        Dl_info info;
        if (dladdr((__bridge void *)cls, &info) && info.dli_fname) {
            NSString *imagePath = [NSString stringWithUTF8String:info.dli_fname];
            NSString *imageName = [imagePath lastPathComponent];
            
            if (!classesByImage[imageName]) {
                classesByImage[imageName] = [NSMutableArray array];
            }
            [classesByImage[imageName] addObject:NSStringFromClass(cls)];
        }
    }
    
    free(classes);
    
    [results addObject:[NSString stringWithFormat:@"总类数: %u", classCount]];
    
    // 按库统计类数量,找出类最多的库
    NSArray *sortedImages = [classesByImage keysSortedByValueUsingComparator:
        ^NSComparisonResult(NSMutableArray *a1, NSMutableArray *a2) {
            return [@(a2.count) compare:@(a1.count)];
        }];
    
    [results addObject:@"各模块类数量 (Top 10):"];
    NSUInteger showCount = MIN(10, sortedImages.count);
    for (NSUInteger i = 0; i < showCount; i++) {
        NSString *image = sortedImages[i];
        NSArray *imageClasses = classesByImage[image];
        [results addObject:[NSString stringWithFormat:@"  %@: %lu 个类", 
                           image, (unsigned long)imageClasses.count]];
    }
    
    // 警告
    if (classCount > 2000) {
        [results addObject:@"⚠️ 警告: ObjC 类数量过多,影响 ObjC Setup 耗时"];
        [results addObject:@"   建议: 删除无用类、使用 Swift struct、合并小类"];
    }
    
    return results;
}

#pragma mark - +load 方法检查

+ (NSArray<NSString *> *)checkLoadMethods {
    NSMutableArray *results = [NSMutableArray array];
    NSMutableArray<NSString *> *classesWithLoad = [NSMutableArray array];
    NSMutableArray<NSString *> *categoriesWithLoad = [NSMutableArray array];
    
    unsigned int classCount = 0;
    Class *classes = objc_copyClassList(&classCount);
    
    for (unsigned int i = 0; i < classCount; i++) {
        Class cls = classes[i];
        Class metaClass = object_getClass(cls);
        
        // 检查类本身的 +load(不包括继承的)
        unsigned int methodCount = 0;
        Method *methods = class_copyMethodList(metaClass, &methodCount);
        
        BOOL hasLoad = NO;
        for (unsigned int j = 0; j < methodCount; j++) {
            SEL sel = method_getName(methods[j]);
            if (sel_isEqual(sel, @selector(load))) {
                hasLoad = YES;
                break;
            }
        }
        
        if (methods) free(methods);
        
        if (hasLoad) {
            NSString *className = NSStringFromClass(cls);
            
            // 简单判断是否是 Category 的 +load
            // (实际上需要更复杂的检测)
            if ([className containsString:@"("]) {
                [categoriesWithLoad addObject:className];
            } else {
                [classesWithLoad addObject:className];
            }
        }
    }
    
    free(classes);
    
    NSUInteger totalLoad = classesWithLoad.count + categoriesWithLoad.count;
    [results addObject:[NSString stringWithFormat:@"+load 方法总数: %lu", 
                       (unsigned long)totalLoad]];
    [results addObject:[NSString stringWithFormat:@"  类的 +load: %lu", 
                       (unsigned long)classesWithLoad.count]];
    [results addObject:[NSString stringWithFormat:@"  Category 的 +load: %lu", 
                       (unsigned long)categoriesWithLoad.count]];
    
    // 列出所有 +load
    if (classesWithLoad.count > 0) {
        [results addObject:@"有 +load 的类:"];
        for (NSString *cls in classesWithLoad) {
            [results addObject:[NSString stringWithFormat:@"  • %@", cls]];
        }
    }
    
    // 警告
    if (totalLoad > 10) {
        [results addObject:@"⚠️ 警告: +load 方法过多,严重影响启动速度"];
        [results addObject:@"   建议: 迁移到 +initialize 或 dispatch_once"];
    }
    
    return results;
}

#pragma mark - 大型初始化器检查

+ (NSArray<NSString *> *)checkLargeInitializers {
    NSMutableArray *results = [NSMutableArray array];
    
    // 这部分需要配合 DYLD_PRINT_STATISTICS_DETAILS 环境变量
    // 或使用 Instruments 分析
    
    [results addObject:@"请使用以下方法分析初始化耗时:"];
    [results addObject:@"1. 设置环境变量 DYLD_PRINT_STATISTICS_DETAILS=1"];
    [results addObject:@"2. 使用 Instruments - App Launch"];
    [results addObject:@"3. 使用 os_signpost 自定义打点"];
    
    return results;
}

@end

8.3 启动耗时打点系统

// LaunchProfiler.h
#import <Foundation/Foundation.h>

@interface LaunchProfiler : NSObject

+ (instancetype)shared;

// 标记阶段开始
- (void)markStart:(NSString *)stage;
// 标记阶段结束
- (void)markEnd:(NSString *)stage;
// 标记时间点
- (void)markMilestone:(NSString *)name;

// 生成报告
- (void)printReport;
- (NSDictionary *)getReport;

@end

// LaunchProfiler.m
#import "LaunchProfiler.h"
#import <mach/mach_time.h>
#import <os/signpost.h>

@interface LaunchStage : NSObject
@property (nonatomic, assign) uint64_t startTime;
@property (nonatomic, assign) uint64_t endTime;
@property (nonatomic, copy) NSString *name;
@end

@implementation LaunchStage
- (double)durationMs {
    static mach_timebase_info_data_t timebase;
    if (timebase.denom == 0) {
        mach_timebase_info(&timebase);
    }
    uint64_t elapsed = self.endTime - self.startTime;
    return (double)elapsed * timebase.numer / timebase.denom / 1000000.0;
}
@end

@interface LaunchProfiler ()
@property (nonatomic, strong) NSMutableDictionary<NSString *, LaunchStage *> *stages;
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *milestones;
@property (nonatomic, assign) uint64_t processStartTime;
@property (nonatomic, strong) os_log_t log;
@end

@implementation LaunchProfiler

+ (instancetype)shared {
    static LaunchProfiler *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[LaunchProfiler alloc] init];
    });
    return instance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _stages = [NSMutableDictionary dictionary];
        _milestones = [NSMutableArray array];
        _processStartTime = [self getProcessStartTime];
        _log = os_log_create("com.app.launch", "profiler");
    }
    return self;
}

- (uint64_t)getProcessStartTime {
    // 获取进程启动时间
    struct kinfo_proc info;
    size_t size = sizeof(info);
    int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
    
    if (sysctl(mib, 4, &info, &size, NULL, 0) == 0) {
        struct timeval startTime = info.kp_proc.p_starttime;
        return startTime.tv_sec * 1000000000ULL + startTime.tv_usec * 1000ULL;
    }
    return mach_absolute_time();
}

- (void)markStart:(NSString *)stage {
    uint64_t now = mach_absolute_time();
    
    LaunchStage *stageObj = [[LaunchStage alloc] init];
    stageObj.name = stage;
    stageObj.startTime = now;
    self.stages[stage] = stageObj;
    
    // os_signpost 集成 (Instruments 可视化)
    if (@available(iOS 12.0, *)) {
        os_signpost_interval_begin(self.log, OS_SIGNPOST_ID_EXCLUSIVE, 
                                   "Launch", "%{public}s", stage.UTF8String);
    }
}

- (void)markEnd:(NSString *)stage {
    uint64_t now = mach_absolute_time();
    
    LaunchStage *stageObj = self.stages[stage];
    if (stageObj) {
        stageObj.endTime = now;
    }
    
    if (@available(iOS 12.0, *)) {
        os_signpost_interval_end(self.log, OS_SIGNPOST_ID_EXCLUSIVE, 
                                 "Launch", "%{public}s", stage.UTF8String);
    }
}

- (void)markMilestone:(NSString *)name {
    uint64_t now = mach_absolute_time();
    
    static mach_timebase_info_data_t timebase;
    if (timebase.denom == 0) {
        mach_timebase_info(&timebase);
    }
    
    uint64_t elapsed = now - self.processStartTime;
    double ms = (double)elapsed * timebase.numer / timebase.denom / 1000000.0;
    
    [self.milestones addObject:@{
        @"name": name,
        @"time": @(ms)
    }];
    
    if (@available(iOS 12.0, *)) {
        os_signpost_event_emit(self.log, OS_SIGNPOST_ID_EXCLUSIVE, 
                               "Milestone", "%{public}s at %.2fms", 
                               name.UTF8String, ms);
    }
}

- (void)printReport {
    NSLog(@"\n========== 启动耗时报告 ==========");
    
    // 按开始时间排序阶段
    NSArray *sortedStages = [self.stages.allValues sortedArrayUsingComparator:
        ^NSComparisonResult(LaunchStage *s1, LaunchStage *s2) {
            return s1.startTime < s2.startTime ? NSOrderedAscending : NSOrderedDescending;
        }];
    
    double totalTime = 0;
    for (LaunchStage *stage in sortedStages) {
        if (stage.endTime > 0) {
            double duration = [stage durationMs];
            totalTime += duration;
            NSLog(@"  %@: %.2f ms", stage.name, duration);
        }
    }
    
    NSLog(@"\n里程碑时间点:");
    for (NSDictionary *milestone in self.milestones) {
        NSLog(@"  [%.2f ms] %@", 
              [milestone[@"time"] doubleValue], 
              milestone[@"name"]);
    }
    
    NSLog(@"\n各阶段总耗时: %.2f ms", totalTime);
    NSLog(@"===================================\n");
}

- (NSDictionary *)getReport {
    NSMutableDictionary *report = [NSMutableDictionary dictionary];
    
    NSMutableArray *stageReports = [NSMutableArray array];
    for (LaunchStage *stage in self.stages.allValues) {
        if (stage.endTime > 0) {
            [stageReports addObject:@{
                @"name": stage.name,
                @"duration": @([stage durationMs])
            }];
        }
    }
    
    report[@"stages"] = stageReports;
    report[@"milestones"] = self.milestones;
    
    return report;
}

@end

8.4 在 App 中使用

// main.m
#import "LaunchProfiler.h"

int main(int argc, char * argv[]) {
    [[LaunchProfiler shared] markMilestone:@"main() 开始"];
    
    NSString * appDelegateClassName;
    @autoreleasepool {
        [[LaunchProfiler shared] markMilestone:@"autoreleasepool 进入"];
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    
    [[LaunchProfiler shared] markStart:@"UIApplicationMain"];
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

// AppDelegate.m
- (BOOL)application:(UIApplication *)application 
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [[LaunchProfiler shared] markEnd:@"UIApplicationMain"];
    [[LaunchProfiler shared] markMilestone:@"didFinishLaunching 开始"];
    
    // 配置工作
    [[LaunchProfiler shared] markStart:@"初始化配置"];
    [self setupConfiguration];
    [[LaunchProfiler shared] markEnd:@"初始化配置"];
    
    // SDK 初始化
    [[LaunchProfiler shared] markStart:@"SDK 初始化"];
    [self initializeSDKs];
    [[LaunchProfiler shared] markEnd:@"SDK 初始化"];
    
    // UI 配置
    [[LaunchProfiler shared] markStart:@"UI 配置"];
    [self setupUI];
    [[LaunchProfiler shared] markEnd:@"UI 配置"];
    
    [[LaunchProfiler shared] markMilestone:@"didFinishLaunching 结束"];
    
    // 延迟打印报告
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), 
                   dispatch_get_main_queue(), ^{
        [[LaunchProfiler shared] printReport];
    });
    
    return YES;
}

// ViewController.m
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [[LaunchProfiler shared] markMilestone:@"首屏渲染完成"];
        [[LaunchProfiler shared] printReport];
    });
}

九、高级优化技巧

9.1 二进制重排

┌─────────────────────────────────────────────────────────────────────┐
│                    二进制重排优化                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   【问题】                                                           │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  代码在二进制中的排列顺序是编译顺序(文件顺序)               │   │
│   │  启动时需要的代码可能分散在多个内存页中                       │   │
│   │                                                              │   │
│   │  Page 1    Page 2    Page 3    Page 4    Page 5              │   │
│   │  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐               │   │
│   │  │A.m  │  │B.m  │  │C.m  │  │D.m  │  │E.m  │               │   │
│   │  │启动✓│  │     │  │启动✓│  │     │  │启动✓│               │   │
│   │  └─────┘  └─────┘  └─────┘  └─────┘  └─────┘               │   │
│   │                                                              │   │
│   │  启动需要加载 3 个页面,产生 3 次 Page Fault                 │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   【优化后】                                                         │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  将启动需要的函数放在一起                                    │   │
│   │                                                              │   │
│   │  Page 1          Page 2    Page 3    Page 4    Page 5        │   │
│   │  ┌───────────┐  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐         │   │
│   │  │A.m 启动✓  │  │B.m  │  │     │  │D.m  │  │     │         │   │
│   │  │C.m 启动✓  │  │     │  │     │  │     │  │     │         │   │
│   │  │E.m 启动✓  │  │     │  │     │  │     │  │     │         │   │
│   │  └───────────┘  └─────┘  └─────┘  └─────┘  └─────┘         │   │
│   │                                                              │   │
│   │  启动只需加载 1 个页面,1 次 Page Fault                      │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

9.2 获取启动时调用的函数

// 方法 1: 使用 Clang SanitizerCoverage
// Build Settings:
// Other C Flags: -fsanitize-coverage=func,trace-pc-guard
// Other Swift Flags: -sanitize-coverage=func -sanitize=undefined

// 回调函数
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
    static uint64_t N;
    if (start == stop || *start) return;
    for (uint32_t *x = start; x < stop; x++) {
        *x = ++N;
    }
}

static NSMutableArray<NSString *> *sCoveredFunctions;
static OSSpinLock sLock = OS_SPINLOCK_INIT;

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
    if (!*guard) return;
    
    void *PC = __builtin_return_address(0);
    
    Dl_info info;
    dladdr(PC, &info);
    
    OSSpinLockLock(&sLock);
    if (!sCoveredFunctions) {
        sCoveredFunctions = [NSMutableArray array];
    }
    
    NSString *funcName = @(info.dli_sname ?: "unknown");
    if (![sCoveredFunctions containsObject:funcName]) {
        [sCoveredFunctions addObject:funcName];
    }
    OSSpinLockUnlock(&sLock);
    
    *guard = 0;
}

// 导出启动时调用的函数列表
void exportLaunchFunctions(void) {
    NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"launch_functions.txt"];
    NSString *content = [sCoveredFunctions componentsJoinedByString:@"\n"];
    [content writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
    NSLog(@"导出到: %@", path);
}

9.3 生成 Order File

# 1. 收集启动时调用的符号(从上面的方法获取)

# 2. 创建 order 文件
# launch.order 内容示例:
_main
_applicationDidFinishLaunching
-[AppDelegate application:didFinishLaunchingWithOptions:]
-[RootViewController viewDidLoad]
-[NetworkManager shared]
...

# 3. 配置 Xcode
# Build Settings → Linking → Order File
# $(PROJECT_DIR)/launch.order

9.4 App 预热 (App Prewarming)

┌─────────────────────────────────────────────────────────────────────┐
│                    iOS 15+ App 预热                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   iOS 15 引入了 App 预热机制:                                       │
│                                                                      │
│   • 系统会预测用户可能打开的 App                                     │
│   • 提前在后台执行 dyld 加载过程                                     │
│   • 当用户真正打开时,直接从预热状态恢复                             │
│                                                                      │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  预热流程:                                                  │   │
│   │                                                              │   │
│   │  系统预测 ─→ dyld 加载 ─→ 运行到 main() 前 ─→ 暂停          │   │
│   │                               │                              │   │
│   │                               ▼                              │   │
│   │                     用户点击 App 图标                        │   │
│   │                               │                              │   │
│   │                               ▼                              │   │
│   │                      从暂停点继续执行                        │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   检测预热启动:                                                     │
│                                                                      │
│   if (@available(iOS 15.0, *)) {                                    │
│       NSDictionary *env = [[NSProcessInfo processInfo] environment];│
│       BOOL isPrewarmed = [env[@"ActivePrewarm"] boolValue];         │
│       if (isPrewarmed) {                                            │
│           NSLog(@"这是预热启动");                                   │
│       }                                                              │
│   }                                                                  │
│                                                                      │
│   注意事项:                                                         │
│   • 预热时不应该执行网络请求                                         │
│   • 预热时不应该显示 UI                                             │
│   • 预热时不应该做用户相关的初始化                                   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
// 处理预热启动
@implementation AppDelegate

- (BOOL)application:(UIApplication *)application 
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    BOOL isPrewarmed = NO;
    if (@available(iOS 15.0, *)) {
        isPrewarmed = [[[NSProcessInfo processInfo] environment][@"ActivePrewarm"] boolValue];
    }
    
    if (isPrewarmed) {
        // 预热启动,延迟某些初始化
        NSLog(@"预热启动,延迟初始化");
        [self deferredInitialization];
    } else {
        // 正常启动
        [self normalInitialization];
    }
    
    return YES;
}

- (void)deferredInitialization {
    // 只做最基础的初始化
    // 其他工作等用户真正使用时再做
    dispatch_async(dispatch_get_main_queue(), ^{
        [self normalInitialization];
    });
}

- (void)normalInitialization {
    // 完整的初始化流程
    [self initializeAnalytics];
    [self initializeNetworking];
    [self setupUI];
}

@end

十、总结与最佳实践

10.1 优化检查清单

┌─────────────────────────────────────────────────────────────────────┐
│                    启动优化检查清单                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   【动态库优化】                                                     │
│   □ 自定义动态库数量 ≤ 6                                            │
│   □ 小型工具库转为静态链接                                          │
│   □ 非必要库延迟加载                                                │
│   □ 合并功能相近的 Framework                                        │
│                                                                      │
│   【Rebase/Binding 优化】                                            │
│   □ ObjC 类数量合理(建议 < 2000)                                  │
│   □ 减少 C++ 虚函数                                                 │
│   □ 优先使用 Swift 值类型                                           │
│   □ 减少全局变量和静态变量                                          │
│                                                                      │
│   【ObjC Setup 优化】                                                │
│   □ 删除无用类                                                      │
│   □ 合并 Category                                                   │
│   □ 减少协议数量                                                    │
│   □ Swift 新代码优先用 struct                                       │
│                                                                      │
│   【Initializer 优化】                                               │
│   □ +load 方法数量 ≤ 10                                             │
│   □ +load 迁移到 +initialize                                        │
│   □ 避免 __attribute__((constructor))                               │
│   □ C++ 全局对象使用懒加载                                          │
│                                                                      │
│   【main() 后优化】                                                  │
│   □ didFinishLaunching 中只做必要工作                               │
│   □ 非首屏 SDK 延迟初始化                                           │
│   □ 首屏 UI 尽量简单                                                │
│   □ 避免同步网络请求                                                │
│   □ 避免大量 IO 操作                                                │
│   □ 图片使用合适大小和格式                                          │
│                                                                      │
│   【高级优化】                                                       │
│   □ 二进制重排(Order File)                                        │
│   □ 处理 App 预热(iOS 15+)                                        │
│   □ 使用 MetricKit 监控线上数据                                     │
│                                                                      │
│   【目标】                                                           │
│   □ 冷启动 pre-main < 400ms                                         │
│   □ 冷启动总时间 < 1s                                               │
│   □ 热启动 < 200ms                                                  │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

10.2 常用工具汇总

工具用途使用方式
DYLD_PRINT_STATISTICS查看 pre-main 各阶段耗时环境变量
DYLD_PRINT_LIBRARIES查看加载的动态库环境变量
Instruments - App Launch完整启动分析Xcode
Instruments - Time ProfilerCPU 热点分析Xcode
Instruments - System Trace系统调用分析Xcode
otool -L查看动态库依赖命令行
nm查看符号命令行
size查看 section 大小命令行
LinkMap分析包体积组成Xcode 输出
MetricKit线上性能监控iOS API

10.3 持续优化建议

┌─────────────────────────────────────────────────────────────────────┐
│                    持续优化流程                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   ┌─────────────┐                                                   │
│   │  建立基线   │                                                   │
│   │  测量数据   │                                                   │
│   └──────┬──────┘                                                   │
│          │                                                           │
│          ▼                                                           │
│   ┌─────────────┐                                                   │
│   │  分析问题   │                                                   │
│   │  定位瓶颈   │                                                   │
│   └──────┬──────┘                                                   │
│          │                                                           │
│          ▼                                                           │
│   ┌─────────────┐                                                   │
│   │  制定方案   │                                                   │
│   │  优先排序   │                                                   │
│   └──────┬──────┘                                                   │
│          │                                                           │
│          ▼                                                           │
│   ┌─────────────┐                                                   │
│   │  实施优化   │                                                   │
│   │  A/B 测试   │                                                   │
│   └──────┬──────┘                                                   │
│          │                                                           │
│          ▼                                                           │
│   ┌─────────────┐                                                   │
│   │  验证效果   │                                                   │
│   │  收集反馈   │────────────────────────────┐                     │
│   └──────┬──────┘                            │                     │
│          │                                   │                     │
│          ▼                                   │                     │
│   ┌─────────────┐                            │                     │
│   │  线上监控   │                            │                     │
│   │  持续跟踪   │────────────────────────────┘                     │
│   └─────────────┘                                                   │
│                                                                      │
│   关键指标:                                                          │
│   • P50/P90/P99 启动时间                                            │
│   • 启动超时率(>2s 占比)                                          │
│   • 不同机型/系统的启动时间分布                                      │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值