Introduction:

I’ve been reading a lot of blogs and articles about startup optimization and I think I’ve learned a lot. However, when we really do this, we will find that the actual situation is very different from the article.
  • A lot of blogs are throughXcodeBuilt-in toolsinstrumentTo analyze problems and find performance bottlenecks. But in the actual operation, I can not use it well, and I can not understand the data indicators.
  • Most of the so-called optimization points in blogs don’t make much difference when they’re actually done.
Through this practical operation, realized some things, here would like to share with you

To analyze problems

There are too many original rational things that have been introduced in the article. There is no explanation here.
For us, startup optimization is about figuring it outpre-mainandafter-mainThe duration of the two phases

Step 1: Try to analyze the bottleneck

Here are two ways to analyze, the actual operation can be selected according to needs

1.Xcode environment variable + manual burying point

pre-mainStage analysis:

configurationXcodeThe environment variable

In Xcode Edit scheme -> Run -> Arguments -> Environment Variables add: DYLD_PRINT_STATISTICS

The Xcode console can then print out the elapsed time of the pre-main phase:

As you can see from the image above, the pre-main phase takes a lot of time (Apple suggests 400ms for the entire startup phase), and even without the system dynamic library, we have a lot of room for optimization.

But there’s really not much we can do at this stage.
Dylib loading Time Dynamic library loading time rebase/ Binding Time Modifyinternal/external pointer ObjC setup time ObjC setup timeCopy the code
For the above three, there’s not much you can do about it other than remove useless code and libraries.

Our project is a 10-year old project, in one version I deleted a large number of third-party libraries, there are still about 80 third-party libraries.

Each of these libraries will have more or less scenarios and functionality in use in the project.

Removing useless code is not cost-effective, and unless you can delete thousands of files, the effect is insignificant.

about+(void)loadmethods

As you can see in the figure above, our main executables take a lot of time in the intializers phase, where loading of image images, +(void)load methods and c++ virtual functions are the key

This is where most blogs talk about avoiding +(void) loads and c++ virtual functions

But why are +load methods and c++ virtual functions time-consuming?
The reason is that at startup, classes are generallyLazy loadingIs loaded only when it is needed.
However,+(void)loadMethod destroys thisLazy loading.
At compile time, any class or category that contains a load method is marked and stored in an executable file_objc_nlclistIn this set.
When it starts up,dyldAfter loadingimageAfter, it will uniformly traverse the collection, initialize the corresponding class inside, and then call the corresponding class+(void)load.
So call+(void)loadMethod, if other classes are involved, don’t you start breaking lazy loading again?
C++ functions are called in the dyld phase.

The diagram below:

How do I find in a project+(void)loadmethods

Add OBJC_PRINT_LOAD_METHODS to the Xcode environment variable to see all the classes that implement the load method on the console.

😂 good guy, our project printed out a total of more than 50 load methods, instantly not calm.

about+(void)loadOptimization space of methods.

The +(void)load method is mostly used for faceted programming, where method swaps are performed.

I want to make two points here.

  • Is it really necessary to optimize this?

    If I hook viewWillAppear on UIViewController to make it bury-free, do I need to optimize the +load method? Because when the first page comes up, you definitely need to load UIKit related classes. This time is wasted in pre-main or after-main. Therefore, my personal opinion is not necessary (but if there are other time points in it, you can consider optimization).Copy the code
  • How to replace?

    There are many blogs on the web that say you can use the +initialize method instead. I don't know if they've actually done it. I think it's risky. Take the traceless burial spot above for example. If I add a hook to the +initialize method, how do I ensure that my class's +initialize method will not be overridden by other classes? I also need it urgently, because there are more than 40 load methods waiting to be replaced in the project.Copy the code

after-mainPhase analysis

Manual buried point

Write a simple method of your own, then insert method calls in various locations, get the time spent on all startup items or phases (the current time minus the last time, get the interval)

After doing this, you can get all the things you want to analyze

After several operations, it is found that several third-party initialization is time-consuming:

Open network monitor umMON ads SDK Carplay initialization AppsFlyers......Copy the code

2. The use ofinstrumentTo analyze.

App Launch is available in the latest instrument.

After starting instrument, the following figure is obtained:

Switch threads, you can see the figure below, aboutLoadMethod time. And that’s what it takes84ms, accounting for than25.5%(Real iPhone 11).

The following isafter-mainPart of the startup phase is time-consuming. It took177ms, accounting for than25.3%

Optimal solution

From the above two methods, it can be seen that the values obtained by the two methods are also very different. This guess has something to do with how Xcode works. But in any case, you can analyze the time. That’s enough for us.

1. Take out+(void)loadmethods

There is no way to optimize the +(void)load method in third-party libraries.

We can optimize only relevant methods in our own projects.

Here’s a handy feature of Xcode. Xcode’s search is powerful enough to write a simple regular expression to match.

Once you have these methods, you can tailor them to your business scenario. This step, we ended up getting rid of 11.

2. Optimizeafter-mainphase

Before optimization, we had about 50 events in our startup phase (tripartite library initialization, network request, singleton initialization…). .

2.1 According to business scenarios, these events are deleted and graded, and the following four opportunities are selected:

  • The App launcheddidFinishIt has to be done
  • Home pagedidLoadIt can be done
  • Home pagedidAppearIt can be done
  • Home page3 safter

After this process, there is no obvious effect, but the business is more clear.

2.2 Asynchronous Initialization

During startup, a serial queue is created where all events that do not need to be placed on the main thread are executed asynchronously.

After this operation, the effect is very obvious. In the current testing situation, the startup phase took little time. It is found that this step saves about 800ms by running the real machine.

Here’s an optimized comparison (iPhone11)

Note: After-main outputs a time when the home page is loaded successfully and a time when the difference is made (the advertising logic is temporarily removed for analysis).

It didn’t actually decrease as much as it did in the process, it actually decreased400-500msThe left and right sides.

Other optimization measures

  • Boot binary rearrangement (this is more about cold boot time, which has been used in our project)
  • Delete useless classes
  • Reduce the operation of pictures related to the home page (for example, can we find a way to decode some preset large pictures and then store them locally, with lossless compression of pictures)
  • Optimize home page logic: remove unnecessary operations, asynchronous time-consuming operations, lazy loading…
  • Make the most of your AD time by removing all the requests and logic from the front page when displaying your AD.
  • Reduce the dynamic library (tested a Kuaishang SDK, which took about 80ms in the startup stage)
  • ………………………

conclusion

Personally, the startup time optimization to come out of the results, mainly depends on the optimization of business logic. Optimizing business logic is a time-consuming and risky business. Be sure to notify your tests of regression after optimization.