建筑师之家IOS / NOTES
← 全部笔记

性能

启动时间要拆开看

pre-main、main 到首帧、首帧之后,这三段不是同一个问题,优化手段也完全不同。

把启动说成一个毫秒数,后面几乎没法改。用户感觉到的是点图标到能点第一下。这条时间里至少有三段:进 main 之前、main 到第一帧提交、第一帧之后你还在主线程上补的初始化。

进 main 之前

这一段主要是 dyld 加载镜像、绑定符号、跑静态初始化和 Objective-C 的 +load。Swift 的全局 let、静态属性,只要在加载时被求值,也会算在这里。+load 比 +initialize 更伤:前者在 main 之前无条件执行,后者要等到类第一次收到消息。

先确认时间是不是花在这里。调试时可以打开 DYLD_PRINT_STATISTICS 和 DYLD_PRINT_STATISTICS_DETAILS,看 total time、total images、还有每个镜像的 initializer。发布包用 MetricKit 的 histogrammedTimeToFirstDraw,或者 Instruments 的 App Launch 模板。不要拿 Debug + 调试器附着的数字做结论,那个数字通常差一截。

main 到第一帧

UIApplicationMain 之后,场景连上、根控制器装好、第一帧提交,这段才是界面出现。故事板或根控制器的 init、viewDidLoad 里同步读磁盘、解大 JSON、创建全部子页面,都会顶在第一帧前面。

原则很简单:第一帧只做能把界面摆出来的事。统计、推送、定位、远程配置、数据库迁移,都排到首帧之后。需要迁移时,界面上可以是一个明确的等待态,而不是让系统启动图多挂两秒。

func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    window?.rootViewController = RootViewController()
    window?.makeKeyAndVisible()
    // 首帧提交后再做,不占用 time-to-first-draw
    DispatchQueue.main.async {
        AppServices.startAfterFirstFrame()
    }
    return true
}

首帧之后仍会感觉慢

第一帧出来了,但主线程接着做同步布局、解码、建表,手指点下去要等。这已经不是启动指标,是首屏可交互。优化时把“能看见”和“能用”分成两个 signpost,别用一个总时间掩盖。

改的时候一次只动一处:先拿掉一个 +load,或把一个 SDK 挪出 didFinishLaunching,再量。一起改五个库,数字变了也不知道是谁。