1 startup type
Cold Launch
What happens when you open the app for the first time or when you launch it again after it has been completely closed. During a cold start, the app needs to be reinitialized and load necessary resources and data.
Warm Launch
The App is already running in the background and what happens when the App is launched again. The survival time of the App in the background is the maximum time interval during which the App can perform a warm start.
2 Start the process
Apple’s official “WWDC Optimizing App Startup Time” divides the startup of iOS applications into two stages: pre-main stage and main stage.
2.2 pre-main stage
During the “pre-main” phase, key steps in the iOS app’s launch process:
1. Load and parse the info.plist file
The system will load and parse the application’s Info.plist file, which contains the application’s configuration information, such as the application’s icon, startup image, permission requirements, etc.
2. Create a sandbox
In iOS 8 and later versions, a new sandbox path is generated every time the application is launched to store the application’s data, settings files, etc. This sandbox is the application’s private storage space.
3. Permission check
According to the permission requirements configured in the Info.plist file, the system will check the status of various permissions applied for by the application, such as permissions to access the camera, positioning, notifications, etc.
4. Load the Mach-O file and run the dynamic link editor ( Dynamic Link Editor, referred to as dyld )
- dyld looks for a suitable CPU operating environment;
- The dyld loader compiles the dynamic libraries (system dynamic libraries and third-party dynamic libraries) and source code files (.h/.m) that the program depends on to generate target files (.o), and connects these dynamic libraries;
- Load all methods. At this stage, the Runtime is initialized and the memory layout of Objective-C is completed, including loading all classes, methods, etc.;
- Load C function;
- Load class extensions and categories. At this stage, Runtime will initialize all class structures, including extensions and categories of loaded classes;
- Load C++ static functions and execute the +load method of Objective-C classes;
- Finally, the main function is called and the main logic of the application begins execution.
Mach-O (Mach Object) is an executable file format used on Apple operating systems such as macOS and iOS, similar to the PE (Portable Executable) file format on Windows.During the compilation process, the source code is processed by the compiler to generate object files (.o files), and then the linker links these object files and the required dynamic libraries into the final Mach-O executable file. This process usually includes steps such as symbol resolution, relocation, symbol table generation, etc., and finally generates a complete executable file for the operating system to load and execute.
Factors affecting startup time :
- Number of dynamic libraries
- The number of OC categories, the number of Category
- Constructor functions in C/C++
- C++ static objects
- +load method of OC class
Optimization :
1. Reduce dynamic library loading,
- Remove unnecessary dynamic libraries
- Merge multiple dynamic libraries into one dynamic library
2. Reduce useless classes or methods
- Clean up useless classes and methods in the project, including useless Category
- Clean up useless static variables and scan them through AppCode code
3. Reduce the number of C++ static objects
4. Check the +load() method
- To avoid unnecessary operations at startup, you can execute them after the first screen rendering is completed.
- Use
+initialize()
method to replace
Print the startup time of the App
Print the corresponding time log on the console
Project→ Scheme → Edit Scheme → Run → Arguments → Environment Variables
Set parameters DYLD_PRINT_STATISTICS = 1
and print detailed informationDYLD_PRINT_STATISTICS_DETAILS = 1
Total pre-main time: 770.45 milliseconds (100.0%)
dylib loading time: 150.62 milliseconds (19.5%)
rebase/binding time: 6.02 milliseconds (0.7%)
ObjC setup time: 9.22 milliseconds (1.1%)
initializer time: 604.57 milliseconds (78.4%)
slowest intializers :
libMainThreadChecker.dylib : 45.22 milliseconds (5.8%)
libglInterpose.dylib : 428.37 milliseconds (55.6%)
PuddingPlus : 189.86 milliseconds (24.6%)
- dylib loading time: Load dynamic libraries, including system dynamic libraries and third-party dynamic libraries.
- rebase/binding time: address binding.
- ObjC setup time: Register Objc classes, complete the relationship mapping between Objc class names and classes, maintain the relationship mapping of SEL-IMP, inject Protocol/Category, etc. into the host class method list, etc.
- initializer time: call the +load() method of the Objc class and call the constructor of the C++ class.
2.3 main stage
The “main” phase can be split into two smaller phases.
The first phase: from main
the beginning of function execution to the completion of application agent didFinishLaunchingWithOptions
function execution.
Factors affecting startup time :
- The time it takes to execute
main
the function - The time it takes to execute
applicationWillFinishLaunching
the function
Optimization :
- Try not to
main
add time-consuming tasks to the function - Load on demand, only load necessary services for App startup and first-screen rendering, and move non-essential services to load after first-screen rendering is completed
The second stage: the first screen rendering stage.
Factors affecting startup time :
- Such as loading, editing, storing pictures, files and other resources
- Runtime method replacement will cause time consumption, such as +load() execution delay of 4 milliseconds
Optimization :
1. Adopt a caching strategy to preload data to reduce user waiting time.
- If the content of the homepage is not particularly real-time, you can obtain the latest data through the interface (and store it on disk). If this data is inconsistent with the already rendered data, refresh the user interface.
2. Delayed request related interfaces
- Version upgrade reminder or pulling global configuration information. If it is a separate interface, you can delay requesting data. Global configuration information can be cached to the local disk every time the latest remote data is requested. After the application is started, local data is read first and remote data is delayed.
- Split or merge interface requests
- If the data on the homepage is divided into multiple modules (or microservices) and the modules are weakly related, putting all the data in one interface will increase the amount of back-end queries and cause the interface to respond too slowly. It can be divided according to logic. A request is issued in a sub-thread, and after the data is responded to, the relevant preprocessing is completed in the sub-thread, and then returned to the main thread to update the UI; whichever request comes back first will render the data of which module first.
- If the data on the home page is very relevant, you can merge the dependent interfaces into one interface to reduce the number of interface requests.
4. Delayed initialization of third-party services
- The third-party service SDK can be placed in a sub-thread to complete row initialization, but must be placed in the main thread for initialization. It can be delayed for a few seconds before initialization.
- Use skeleton screens and other solutions
- Displaying a skeleton screen during a network request gives the user the impression that the data is about to be displayed.