Walk through what happens from tapping the icon to the first frame.

6.4.1 · L5 · Interview

Predict first

In what order do these print for an Objective-C class with +load, a C constructor and +initialize, and a Swift main that prints “3 main”, then uses the class?

puts("3 main")
_ = Loader()
puts("4 after first use")
  • 3 main, 0 +load, 1 constructor, 2 +initialize, 4
  • 0 +load, 1 constructor, 3 main, 2 +initialize, 4
  • 0 +load, 2 +initialize, 1 constructor, 3 main, 4

Mental model. Opening a restaurant: the building has to be unlocked and the kitchen wired up (pre-main), then the cooks prepare the first dish (post-main), and only then does the customer get a plate.

The system asks the process supervisor to start your app. The kernel executes the binary and hands control to dyld, the dynamic linker, which maps the main executable and every dynamic library it needs, applies fix-ups (rebasing and binding pointers), and then runs initialisers in a defined order. For an Objective-C binary the order is: +load methods first, then C constructors (__attribute__((constructor))) and C++ static initialisers; then main runs. Swift globals are lazy and initialise on first use, and +initialize also runs lazily, just before a class first receives a message.

Everything up to main is “pre-main”. After it, main calls UIApplicationMain, which starts the run loop and calls didFinishLaunching; the scene connects and builds the window and first view hierarchy; layout and the first render pass run; and the result is committed to the render server, which draws the first frame. A cold launch does all of this; a warm launch finds some things cached; a resume from the background is not a launch at all. Since iOS 15 the system may prewarm an app, running some pre-main work before the user taps, which makes pre-main time unreliable to measure on its own.

+load runs as the image loads, then the constructor, both before main. +initialize waits until the class is first used.

Common traps

  • Doing network or database work in didFinishLaunching. It runs before the first frame and delays it directly.
  • Measuring “launch time” with a stopwatch in main. Pre-main time and prewarming are invisible from there.

They might follow up with

  • Where do Swift global variables initialise? Lazily, on first access, and exactly once. They are not part of the pre-main cost.
  • Why is +load discouraged? It runs for every class that defines it, before main, on every launch.

Related questions

  • 6.4.3 Your app launches slowly. How do you find out why and what do you cut?
  • 6.4.2 What does dyld do, and how do static and dynamic linking change launch?
  • 6.2.3 What happens inside UIApplicationMain, and how does @main on a SwiftUI App differ?
  • 4.2.3 What are +load and +initialize, and which can Swift use?

What does dyld do, and how do static and dynamic linking change… →