Foreword
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 process
We all know that the launch of iOS App is divided into 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, pre-main stage
1. Load the executable file of the application
2. Load dyld
dynamic connector
3. dyld
recursively load all dependent dynamic link libraries dylib
2, main() phase
1. dyld
calls main()
2. UIApplicationMain()
3. applicationWillFinishLaunching
4. didFinishLaunchingWithOptions
stage optimization item
1, pre-main stage
pre-main
optimizing for the 06180b0c09c755 stage, we need to understand the loading process in detail. You can learn more about the relevant materials in the Optimizing App Startup Time of WWDC 2016
1.1 Load dylibs
At this stage, dyld
will analyze the application dependent dylib
(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 registered to The kernel, and finally call dylib
for each segment
mmap()
. But individual cases from the dylib
zoomed parts are system libraries, we do not need additional optimization.
optimization conclusion:
1. Try not to use the embedded dylib
, so as to avoid increasing the overhead of Load dylibs
2. Combine the existing dylib
and use the static library (static archives)
to reduce the number of dylib
3. Lazy loading dylib
, but note that dlopen()
may cause some problems, and in fact, lazy loading will do more
1.2 Rebase/Bind
During dylib
, the system introduced the ASLR
(Address Space Layout Randomization) technology and code signature for safety reasons. Due to ASLR
, the image (Image, including executable files, dylib
and bundle
) will be loaded at a random address, and there will be a deviation from preferred_address
slide
), dyld
Deviation, to point to the correct address. Rebase
in front, Bind
in the back, Rebase
does is to read the mirror into the memory and correct the pointer inside the mirror. The performance consumption is mainly in IO. 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.
In this process, we need to pay attention to minimize the number of pointers, such as:
1. Reduce the number of ObjC classes, selectors, and categories
2. Reduce the number of C++ virtual functions (there is overhead to create a virtual function table)
3. Use Swift struct (internal optimization, fewer symbols)
1.3 Objc setup
The ObjC
initialization has been completed in the Rebase/Bind
stage. This step dyld
will register all the declared ObjC
classes, insert the classification into the class method list, and then check the selector
of each 06180b0c09c2.
There is nothing to optimize 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 program ⾏ initialization functions, each transfer Use Objc
class and classifying +load
Remedies, transfer Use C/C++
constructor function (Use in attribute((constructor)
) modified function), and create a substantially comes in handy type C++
Static global variables. Initializers
stage is executed, dyld
starts to call the main()
function.
optimization conclusion:
1, less in class +load
Remedies ⾥ do things, try to postpone these things to +initiailize
2. Reduce the number of constructor functions and do less in the constructor function
3. Reduce the number of constructor functions and do less in the constructor function
main() phase
At this stage, the main optimization focus is on SDK initialization, business tool registration, and the overall didFinishLaunchingWithOptions
method, because some of our third app grid 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:
didFinishLaunchingWithOptions
business needs, try to reduce the event processing logic of the 06180b0c09cae2 method in the main thread, such as:
1, according to the actual traffic situation, each sort ⼆ ⽅ / ⽅ three libraries, libraries find loading can be delayed, the retardation load process, such as into ⽐ therefore especially ⻚ controller viewDidAppear
Remedies ⾥.
2. 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.
3. Avoid some complicated/redundant calculation logic, and try to implement asynchronous delay processing for this kind of logicviewDidLoad
and viewWillAppear
upper controller. After these two methods are executed, the upper controller can display
scene supplement
In addition, in our actual development process, many items of high-level controllers 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 the user’s network status is not a controllable item. If you don’t do other processing, then in many scenarios, even if we put some placeholder images, the display style is very unfriendly. After all, the first visual impact of the high-level controller on the user The impact is still significant.
For optimization in this kind of scenario, generally we can adopt Local + Network + Update
to optimize the high loading speed to a certain extent, namely:
1. In the app update process, advanced local embedded processing logic, embedded in-line data structure ( localDataBase
), embedded in-line style resources ( 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 still necessary to choose a suitable optimization plan according to the actual scene of the project.
Statistical time length
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
edit scheme
setting, and 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. So this recommends a simple monitoring method, that is U-APM performance monitoring SDK . It only needs us to perform simple pod integration, and then we can proceed according to our actual needs. Or automatically monitor the startup data. For details, please refer to U-APM . In order to facilitate our analysis of the data, the Umeng backend has helped us draw the corresponding distribution map based on these data, and we can see it clearly. The distribution of start-up time consumption, start-up type occupancy, etc. are obtained, as shown in the figure:
In addition, we can also use the SDK to perform crash analysis, ANR analysis, monitoring alarms, U-APM
the monitoring platform 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.
reference
dyld loading process from scratch 16180b0c09cced
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。