preface

When the user presses the home button, the iOS App is not killed immediately, but continues to live for some time. Ideally, when the user clicks on the App icon and comes back, the App needs to do very little to return to its original state and continue serving the user. In this case, we call it hot startup. In contrast, cold startup is the process of starting from scratch after the App is killed. We’re only talking about cold startup of the App.

For a cold launch, startup time is the time between the moment the user clicks on the APP and the time the user sees the first screen. When we optimized, we divided the startup time into pre-main time and the time from the main function to the first screen rendering. Since the entry to our APP is in main, our code will execute after main.

There are two stages

1The pre - the main stage1.1. Load the executable file of the application1.2.load dyld (dynamicLoader)1.3.dyld recursively loads the dylib of all dependencies of the application (dynamicLibrary Dynamic link library)2The main () phase2.1.dyld calls main()2.2. CallUIApplicationMain(a)2.3. Call applicationWillFinishLaunching2.4. Call didFinishLaunchingWithOptionsCopy the code

We call the pre-main stage T1, and the main() stage until the first page loads __ is called T2.

Optimization analysis of T1 time

Part T1 mainly refers to a practice of APP startup optimization

T1 Apple provides built-in measurements, and Xcode sets the environment variable DYLD_PRINT_STATISTICS to 1 in Edit scheme -> Run -> uments

/ / for the results
Total pre-main time: 1.4 seconds (100.0%)
         dylib loading time: 1.3 seconds (89.4%)
        rebase/binding time:  36.75 milliseconds (2.5%)
            ObjC setup time:  35.65 milliseconds (2.4%)
           initializer time:  80.97 milliseconds (5.5%)
           slowest intializers :
             libSystem.B.dylib :  12.63 milliseconds (0.8%)
/ / read
1The main() function was used altogether before1.4s

2And in the94.33ms, load dynamic library used1.3s, pointer relocation is used36The 75 ms,ObjCClass initialization is used35.65ms, various initializations used8097 ms.3, in the initialization cost80The most time-consuming initialization in.97ms is libSystem.B.dylib.Copy the code

As you can see, my dylib loading time took 1.3s,

The function of each of these parts is

Load the dylib and analyze each dylib (mostly iOS) to find itMach-OFile, open and read to verify validity, find the code signature registered to the kernel, and finally call mmap() on each segment of dylib.Copy the code
After rebase/bind dylib is loaded, they are independent of each other and need to be bound together. In the process of loading dylib, the system introduced for security reasonsASLR(Address Space Layout Randomization) technical and code signing. Due to theASLRExistence, mirror image (Image, including executables, dylib, and bundles) will be loaded at random addresses, and there will be a slide deviation from the preferred_address, which dyLD will need to correct to point to the correct address.RebaseIn the former,BindIn the back,RebaseWhat is done is to read the image into memory and correct the pointer inside the image. The performance cost is mainly inIO.BindDo is to query the symbol table, set the pointer to the external mirror, performance consumption is mainly inCPUCalculation.Copy the code
OC setup
OCThe Runtime needs to maintain a global table of class names and a list of class methods. Dyld does the following: for all declaredOCClass to register into the global table (class registration) will becategoryIs inserted into the class's method list (category registration) Check eachselectorUniqueness of (selector uniquing)Copy the code
If in each of theOCThe 'load' method of the class does a lot of things (such as using it insideMethodSwizzle), that would take a lot of time. Dyld runAPPThe initialization function of eachOCClass +load method, calledC++ attribute((constructor) modifier) to create non-primitive typesC++ static global variables, and then execute main.Copy the code

The optimization idea is

1. Remove unwanted dynamic libraries2. Remove classes that are not needed3. Merge classes and extensions with similar functionality4Try to avoid performing operations in the +load method and defer them to the +initialize method.Copy the code

Optimization analysis of T2 time

T2 uses a large timer BLStopwatch from NewPan

As you can see, my APP load time is not too slow, but I want to see if there is any room for optimization.

In didFinishLaunchingWithOptions method, we usually have the following logic:

Initializing a Third PartySDKconfigurationAPPRun the initialization of some of the utility classes required by the environment itself...Copy the code

Here mainly refer to [iOS] an immediate startup time optimization as can be seen from the optimization diagram, the jump logic of my application is to open -> Advertising page -> home page, the UI architecture of the home page is:

But if the UI framework as above, and set up the root view in didFinishLaunchingWithOptions

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSLog(@"DidFinishLaunchingWithOptions began to perform");

    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    TestTabBarController *tabBarVc = [TestTabBarController new];
    self.window.rootViewController = tabBarVc;
    [self.window makeKeyAndVisible];

    NSLog(@"didFinishLaunchingWithOptions 跑完了");

    return YES;
}

Copy the code

And then we go to the viewDidLoad method in TestTabBarController and set its viewControllers, And then go into the viewDidLoad method of each viewController and do some more initialization. So what do you think from didFinishLaunchingWithOptions to finally show show viewController viewDidLoad execution order of these methods is what?

DidFinishLaunchingWithOptions began to perform start loadingTestTabBarControllerThe viewDidLoad didFinishLaunchingWithOptions ran the start loadingTestViewControllerViewDidLoad, and do a bunch of initializationCopy the code

If the view of TestViewController is operated on in TestTabBarController, the order of calls will look like this:

DidFinishLaunchingWithOptions began to perform start loadingTestTabBarControllerViewDidLoad starts loadingTestViewControllerViewDidLoad, then perform a heap of initialization operation didFinishLaunchingWithOptions ran outCopy the code

The problem is that when we put interface initialization, network request, data parsing, view rendering, etc. into viewDidLoad, we have to process all of these events before the user sees the first page every time we start the APP before we go to the view rendering phase.

Generally speaking, we in the didFinishLaunchingWithOptions execution of code, there are a lot of initialization, such as log, statistics, the SDK configuration, etc. Try to put only the necessary ones, and the rest can be deferred until the MainViewController displays viewDidAppear.

* Log, statistics and other events that must be configured first when the APP starts * events such as project configuration, environment configuration, initialization of user information, push, IM and other SDK and configuration eventsCopy the code
  • The first category, which must be started first, still leaves it theredidFinishLaunchingWithOptionsIn the start.
  • Second, these functions must be loaded before users enter the main body of the APP. I put them on the advertising pageviewDidAppearStart.
  • Third, since the startup time is not necessary, so we can put in the first interfaceviewDidAppearIn this method, it doesn’t affect the startup time at all.

This is the optimized startup time

Optimization idea

Comb through the tripartite libraries and find libraries that can be lazy loaded, for example, in the viewDidAppear method of the home controller. Sort out the business logic and delay the execution of the logic that can be delayed. Check for new versions, register for push notifications, and so on. Avoid complex/redundant calculations. Avoid doing too much on the home controller's viewDidLoad and viewWillAppear methods. The home controller can't display until these two methods are done. Some views that can be created lazily should be created lazily/loaded lazily. Adopt a better performing API. The home page controller is built in a pure code manner.Copy the code

Also: [iOS] an immediate startup time optimization mentioned using a tool class to manage the method, can be easier to manage optimization.

conclusion

The optimization stage with the highest cost performance is some logical arrangement of T2, and the execution of unnecessary time-consuming operations will be postponed to the first screen display as far as possible. At the same time, in general, optimization should be carried out after the project is completed and stabilized, to avoid premature optimization.

Reference:

  1. App Startup Time: Past, Present, and Future
  2. [iOS] An instant startup time optimization
  3. IOS App startup performance optimization
  4. An exercise in APP startup optimization
  5. Ali data iOS end start speed optimization experience