Preface
As programmers, "performance optimization" is a word that we are all familiar with, and it is also something we need to work on and continue to make; in fact, optimization is a very large topic, because the subdivisions are very large. There are a few optimization directions, but we must not blindly optimize the optimization during the actual development process. This may sometimes cause counterproductive negative effects. We need to make reasonable optimizations based on actual scenarios and business needs. Next, let’s move on to the main topic. This article will focus on the launch optimization of the iOS App as the starting point for discussion.
Start the process
We all know that the launch of iOS App is divided into two stages: pre-main and main(), and in these two stages, the system will carry out a series of loading operations, the process is as follows:
1. The pre-main stage
- Load the executable file of the application
- Load dyld dynamic linker
- dyld recursively loads all dependent dynamic link libraries dylib
2. The main() stage
- dyld calls main()
- Call UIApplicationMain()
- Call applicationWillFinishLaunching
- Call didFinishLaunchingWithOptions
Stage optimization
1. The pre-main stage
When optimizing for the pre-main stage, we need to understand the loading process in detail. This can be learned in detail in the Optimizing App Startup Time of WWDC 2016. Related materials
1.1 Load dylibs
At this stage dyld will analyze the dylib that the application depends on (after xcode7.dylib has been changed to .tbd), find its mach-o files, open and read these files and verify their validity, and then find the code signature Register to the kernel, and finally call mmap() for each segment of dylib. However, most of this dylib is a system library, so we don't need to do additional optimization.
Optimization conclusion:
1. Try not to use the embedded dylib, so as to avoid increasing the overhead of Load dylibs
2. Combine existing dylibs and use static archives to reduce the number of dylibs used
3. Lazy loading of dylib, but be aware that dlopen() may cause some problems, and in fact, lazy loading will do more
1.2 Rebase/Bind
During the loading process of dylib, the system introduced ASLR (Address Space Layout Randomization) technology and code signing for security considerations. Due to the existence of ASLR, the image (Image, including executable files, dylib and bundle) will be loaded at a random address, and there will be a deviation (slide) from the address pointed to by the previous pointer (preferred_address), dyld needs to correct this Deviation, to point to the correct address. Rebase comes before Bind. What Rebase does is to read the mirror into the memory and correct the pointer inside the mirror. The performance consumption is mainly in IO. What Bind does is to query the symbol table and set a pointer to the outside of the mirror. The performance consumption is mainly calculated by the CPU.
Optimization conclusion:
In this process, we need to pay attention to minimize the number of pointers, such as:
- Reduce the number of ObjC classes, selectors, and categories
- Reduce the number of C++ virtual functions (there is overhead to create a virtual function table)
- Use Swift struct (internal optimization, fewer symbols)
1.3 Objc setup
Most of the ObjC initialization work has been completed in the Rebase/Bind phase. In this step, dyld will register all declared ObjC classes, insert the classification into the class method list, and then check the uniqueness of each selector.
There is not much optimization to do at this step. The Rebase/Bind phase is optimized, and the time-consuming step will be reduced.
1.4 Initializers
At this stage, dyld starts to run the initialization function of the program, invokes the +load method of each Objc class and classification, and invokes the constructor function in C/C++ (function decorated with attribute((constructor))) , And create non-basic C++ static global variables. After the Initializers stage is executed, dyld starts to call the main() function.
Optimization conclusion:
- Do less things in the +load method of the class, try to postpone these things to +initiailize
- Reduce the number of constructor functions and do less in the constructor function
- Reduce the number of constructor functions and do less in the constructor function
2. The main() stage
At this stage, the main optimization focus is on SDK initialization, business tool registration, the overall didFinishLaunchingWithOptions method, because of our third-party app configuration, startup boot screen display status logic, version update logic, etc. Basically, everything will be done here. If this part of the logic is not optimized and sorted out, as the business continues to expand, the bloated business logic will directly cause the startup time to increase.
Optimization conclusion:
On the premise of meeting business requirements, try to reduce the event processing logic of the didFinishLaunchingWithOptions method in the main thread, such as:
- According to the actual business situation, sort out the various sub/three libraries, find the libraries that can be lazily loaded, and do the lazy load processing, just like the viewDidAppear method of the in-controller.
- Sort out the business logic, and deal with the delayed execution logic that can be delayed. Logic such as checking for new versions, registering for push notifications, etc.
- Avoid some complicated/redundant calculation logic. This kind of logic should be processed asynchronously and delayed as much as possible
- Avoid doing too many things in the viewDidLoad and viewWillAppear of the upper controller that can easily block the main thread. After these two methods are executed, the upper controller can display
Scene supplement
In addition, in our actual development process, the high-level controllers of many items will have some back-end configurable, richer structure or recommended data for display, and our high-level display speed will usually be incorporated. To start the optimization part, in fact, for this type of optimization, if we only use the traditional api -> data -> UI method, it is difficult to have obvious room for improvement, because of the user's network status It is not a controllable item. If no other processing is done, then in many scenarios, even if we put some placeholder images, the display style is very unfriendly. After all, the controller is compatible with the user. The first visual impact of the user is still relatively large.
For optimization in this scenario, we can generally take the approach of Local + Network + Update to optimize to a certain extent
⻚Loading speed:
which is:
1. During the app update process, advanced local embedded processing logic, embedded local data structure (localDataBase), embedded resource required for local storage (localStorage)
2. After the installation is started, check the local and online data update records to check whether the local embedded data structure needs to be updated
3. When it detects that there is data that needs to be updated, the specified structure will be silently updated, and the local data structure will be updated synchronously
The advantages of this are:
1. Up-to-date data is directly loaded locally, reducing network data waiting time
2. Only detect the change of data key value, reduce the amount of data and the directional update structure, reduce the frequency of api data interaction and the volume of data packets
3. It can ensure that the user will always be in a friendly display state for users
Of course, this is not the only way to deal with it, and it is not applicable to all scenarios. It just provides an idea. It is necessary to choose a suitable optimization plan according to the actual scene of the project.
Statistical time
In addition, if during the development process, we want to visually check the time-consuming situation of each stage during app startup, we can also add DYLD_PRINT_STATISTICS to 1 in the edit scheme setting of Xcode to print the startup time, for example
Startup time before optimization:
Startup time after optimization:
Of course, these logs can only be viewed and printed during the development and debugging stage. In actual projects, we need to monitor the startup data of online items in order to locate and optimize the links that affect the startup time of the app in time. At this time, how should we deal with it better?
Of course, we can conduct self-statistical analysis through the method of reporting on the server, but in this way we will find that our statistical cost will increase significantly, and the result analysis will become less flexible. Therefore, this recommends a simple monitoring method, that is, U-Meng's U-APM should be able to monitor the performance of the SDK. After we only need to integrate a simple pod, we can perform manual or self-control according to our actual needs. For details, please refer to U-APM. In order to facilitate our analysis of the data, the Umeng backend has helped us to draw a corresponding distribution map based on these data, and we can get the startup clearly. Time-consuming distribution, startup type occupancy, etc., as shown in the figure:
1. Start-up time and number of times
2. Start-up time-consuming distribution
In addition, we can also perform crash analysis, ANR analysis, monitoring alarms, freeze analysis, memory analysis and many other functions through the SDK. With the U-APM monitoring platform, it is actually very much in the actual development process. Improved the efficiency of our online app optimization analysis. Of course, the introduction in this text is only a relatively simple optimization item, which is only for reference and guidance of ideas. The road of optimization is a long way to go, and we need to continue to explore, discover, and improve. But at the end, I still want to remind you: in the actual project development process, don't optimize for optimization, but make targeted optimization based on the project situation.
refer to:
Exploring Mach-O files
iOS Bottom Layer-Sorting out the dyld loading process from scratch
iOS app start-dyld loading App process
wwdc2016optimizingappstartuptime.pdf
Author: Wu Yubao
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。