How to Slash iOS App Startup Time: Deep Dive into Dynamic Linking and Initialization
This article examines the factors that slow iOS app launch—especially dynamic library loading and Objective‑C class initialization—and presents practical measurements and optimization techniques to dramatically reduce both cold and hot startup times.
Technical Research
In short, total startup time t(App) = t1 (time before main()) + t2 (time after main()). t1 covers loading system dylibs and the app executable; t2 spans from the end of main() to the completion of Application:didFinishLaunchingWithOptions:, where the first UI is built and rendered.
Loading Process Before main()
When the app starts, the system first loads the executable (all .o files) and then the dynamic linker dyld, which recursively loads all dependent dynamic libraries.
Dynamic libraries include all iOS system frameworks, the Objective‑C runtime library libobjc, and system libraries such as libSystem, libdispatch (GCD) and libsystem_blocks.
What Is an Image?
Executable file (e.g., .o files)
Dynamic library (dylib) or framework bundle
Bundle resources (loaded via dlopen, not recommended)
Benefits of Dynamic Linking
Code sharing: a single copy of libraries is kept in memory and on disk.
Easy maintenance: libraries can be updated without rebuilding the app.
Reduced executable size compared with static linking.
ImageLoader
Each image is loaded by an ImageLoader instance, which first recursively loads dependent images and then resolves all symbols.
Dynamic Library Loading Steps
Load dylibs image – read library images.
Rebase image – fix internal pointers (ASLR).
Bind image – fix external pointers.
ObjC setup – register classes, categories, and selectors.
Initializers – run +load methods, C++ constructors, and non‑trivial static initializers.
Load dylibs image
During this step dyld:
Analyzes dependencies.
Finds the Mach‑O file.
Opens the file.
Verifies the file.
Registers the signature with the system.
Calls mmap() on each segment.
Typical apps load 100‑400 dylibs; system libraries are heavily optimized.
Rebase / Bind
Because of ASLR, the loader must adjust pointers. Rebase fixes pointers inside the image (IO‑bound), while bind resolves external symbols (CPU‑bound).
ObjC Setup
Class registration.
Category registration.
Selector uniquing.
Little room for further optimization here.
Initializers
Static fix‑ups are followed by dynamic adjustments such as:
ObjC +load() methods.
C++ constructor attributes ( __attribute__((constructor))).
Non‑trivial C++ static globals that perform heavy work.
These run from bottom to top to ensure dependencies are available.
Measuring Pre‑main Time
Enable dyld_PRINT_STATISTICS in a real device debug session to obtain detailed timing. Results show system dylibs are fast; the app’s own code accounts for ~94 % of total launch time, with image loading and ObjC class initialization consuming ~79 %.
Optimization Opportunities Before main()
Remove unnecessary frameworks (dynamic linking is costly).
Mark frameworks as required when present on all supported iOS versions; otherwise use optional to avoid extra checks.
Merge or delete unused ObjC classes; use static analysis tools to find dead code.
Eliminate unused static variables and methods.
Move work from +load to +initialize when possible.
Avoid C++ virtual functions to reduce v‑table overhead.
Loading After main()
After main(), the app initializes services and renders the home screen, primarily in application:didFinishLaunchingWithOptions:. Rendering involves three phases: image decoding, layout ( layoutSubviews), and drawing ( drawRect:).
Post‑main Optimizations
Prefer pure code UI over storyboards/xibs.
Assess NSUserDefaults size; split large plists if needed.
Reduce excessive NSLog calls during launch.
Batch network requests and run them asynchronously.
Real‑world Measurements
A minimal HelloWorld project with only pods shows a cold start of ~2 s on iPhone 6 iOS 9.3.5, mainly due to dynamic library loading and symbol binding. A hot start is ~500 ms faster thanks to dyld caching.
Further Findings
NSUserDefaultsinitialization takes only ~1.8 ms, not a bottleneck. The biggest delays are UI construction and rendering (storyboard loading, tab bar/top bar rendering, splash ad loading, cell registration, logging module init).
Specific Optimizations for Toutiao
Build the home UI programmatically instead of using storyboards.
Delay or lazily load non‑essential work in didFinishLaunching, coordinating with product and R&D teams.
Analyze classes with +load and move work to later stages.
Minimize work in viewDidLoad and viewWillAppear of the main feed controller.
Result
After applying these changes, the post‑main launch time met the expected performance targets, as verified by production metrics.
Source: 今日头条技术博客 URL: http://techblog.toutiao.com/archives/2017/01/iosspeed/
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
21CTO
21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
