-
App startup process
The launch of an App usually starts when the user clicks on the App and ends when the App delegate’s didFinishLaunching method completes, usually splitting the launch into a cold launch and a hot launch.
-
Cold start
Cold start:When an App’s process is not in the system before it starts, the system needs to assign a process to it to start. This is a one-time start (generally boot optimization is to optimize the cold start process)
-
Warm start
Thermal activation:It refers to the process of putting the App back into the background after cold startup, when the App process is still in the system and the data of the App is stored in memory, and then starting the App again. This process does very little
-
-
App startup optimization
The above also said that the general start optimization is the main optimization of the cold start process, hot start to do very little. So here’s just the optimization of the cold start process. The cold start process is divided into before and after main
-
main
Before the function is executedThe operating system loads the App executable file into memory, performing a series of load & link work, which can be added by adding environment variables
DYLD_PRINT_STATISTICS
To view themain
What was done before the function was executed, and how much time was spentIt is not difficult to findmain
The following are the main things you do before executing the function- Dynamic library loading
The corresponding isdylib loading time
You can see that the load time is 48.41 milliseconds
Optimization suggestions:
The main optimization suggestion here is to reduce the loading of dynamic libraries. Apple recommends less use of dynamic libraries, and when the number of dynamic libraries is large, try to merge multiple dynamic libraries. In terms of quantity, Apple supports the merger of up to six non-system dynamic libraries - Offset correction and symbolic binding
The corresponding isrebase/binding time
, taking 9.18 milliseconds- The offset correction
Any method or function in an app-generated binary will have an address that is offset from the current binary, but at run time the system will randomly generate a number to add to the header of the binary (ASLR
The security mechanism is explained below), so the address of the function or method isRandomly assigned value + offset addressThis process is called offset correction - Symbol binding
Dynamic libraries are not like static libraries, which are actually generated at compile time with the corresponding codemach-o
In this case, the static library’s methods and functions can be retrieved directly from the corresponding addresses, but the dynamic library is not packaged at compile timemach-o
File, but this time we use methods in the dynamic library, for exampleNSLog
Method, it will generate one! NSLog
At this point, the symbol will be randomly pointed to an address. When run, the dynamic library is loaded into memory. At this point, you can get the addresses of the methods and functions corresponding to the dynamic library! NSLog
This symbol is bound to the corresponding address (dyld
The process is calledSymbol binding
- The offset correction
- A class registration
The corresponding isObjC setup time
, taking 10.86 milliseconds
Optimization Suggestions
Remove classes that will not be used after startup - Execute the load and constructor
The corresponding isinitializer time
, taking 110.79 milliseconds
Optimization Suggestions
To use lessload
Method can be corresponding toload
In the+initialize()
Method, method, method, methodload
Method execution takes 4 milliseconds, and if implemented in the classload
Then the corresponding class will be loaded earlierread_image
Method, if not implementedload
The class is loaded when the method first sends a message,
- Dynamic library loading
-
main
After the function is executedThis stage mainly refers to the start of the main function execution until the first screen rendering completion method execution. The main work in this stage includes:
- The third-party SDK is initialized. Procedure
- Custom utility class initialization
- First screen data loading
- Some calculations for the first screen rendering
The optimization suggestions of this place mainly include the following points
- Only handle the first screen rendering related tasks, other non-first screen services such as initialization, registration listening, configuration file reading and so on are done after the home page rendering is completed, of course, can also open up a thread to deal with these things. Try not to occupy the main thread
- The optimization of their own business logic, the abandoned logic code, methods, functions are deleted, reduce the time of each process
- Try to avoid xiB, storyboard (there will be a transition process in between and it will take time) and use pure code for the MAIN framework of the UI
-
-
The basics of binary rearrangement
The above is mainly for the specific stage to do some optimization processing, in addition to the optimization scheme of deletion, there is another optimization, namely binary rearrangement. Before explaining binary rearrangement, we will first introduce several conceptual things:
-
Physical memory
Is the running memory, refers to the computer installed memory, popularly will actually be the size of the memory bar.
Early operating systems did not have virtual memory, and programs addressed at physical addresses. Therefore, every process that did not start a program had to allocate a corresponding chunk of physical memory to the program. This caused several problems:- When physical memory is allocated, other programs can no longer be loaded into memory (i.e. cannot run), and must wait for other programs to exit and free up memory before new programs can be run
- Program instructions operate on physical memory, so my process can modify other processes’ data, even the kernel address space
In view of the above problem also leads to virtual memory
-
Virtual memory
It refers to the use of a portion of the disk space as memory
Add a middle layer between process and physical memory. This middle layer is called virtual memory, which is mainly used to manage physical memory when multiple processes exist at the same time. Improved CPU utilization, enabling multiple processes to be loaded simultaneously and on demand. So virtual memory is essentially a mapping table of virtual addresses and physical addresses. Each process has a separate virtual memory, which starts at an address of 0 and has a fixed size of 4 gb.
When a process starts to access an address, it might go through the following procedure:- Each time I want to access an address in the address space, but the process cannot access each other, ensuring the security of the data between processes (a process can only access the given address of the virtual memory). You need to translate the address to the actual physical memory address
- All processes share the entire physical memory, and each process maps only the virtual address space it currently needs to the physical memory
- Each virtual memory is divided into one page (page size in iOS is 16K, others are 4K). The process needs to know which address space data is in physical memory, which is not (perhaps stored on disk), and where in physical memory, which needs to be recorded through the page table
- Each entry in the page table is divided into two parts. The first part records whether the page is in physical memory, and the second part records the address of the physical memory page (if so).
- When a process accesses a virtual address, it will first look at the page table, and if the corresponding data is not in physical memory, a page missing exception will occur
- Missing page exception process, operating system, blocking the process immediately and will drive the corresponding page in memory, and then make the process in place, if the memory is full, not empty place, then find a cover page, and as for the concrete cover which page, just need to see how operating system page replacement algorithm is designed.
The following figure shows the relationship between virtual memory and physical memory If physical memory is full and new pages need to be loaded, the new pages will overwrite pages that have not been used for a long time
-
ASLR
The starting address and size of the virtual memory are fixed, which means that when we access the virtual memory, the address of its data is also fixed, which will make our data very easy to be cracked. To solve this problem, Apple introduced ASLR technology in iOS4.3 to solve this problem. The realization principle is to add a random block of address in the head of virtual memory, so that each time the virtual address of the actual address is not the same, so in the program start time need to do offset correction.
-
-
Cause of binary rearrangement
As can be seen from the above knowledge, when the ios program is loaded into virtual memory, it will be divided into many pages. If the corresponding physical address of a page of the virtual address accessed at this time does not exist, there will be a page shortage exception, which will block the process to load this page into the physical memory and then access it. You can go through here
instruments
theSystem Trace
To see the number of page missing exceptions for your project:Step: First click start -> home page after loading complete pause -> then find your project find the main threadThere are more than 200 page missing exceptions before the startup. At this time, we look at the default order of the project at compile time. At this time, we write a simple demo as shown in the figure below:Just write a few simple methods, and then select from the projectBuild-setting
searchlink map
Then configureIn this case, the corresponding configuration folder is generatedlink-map
File,Methods, functions, and so on are discovered in the order they are implemented in the file, and the file order is in accordance withcomple source
Is shown in the following figure:In this case, only one method per page may be useful, and other methods and functions are not called during startup, which leads to a large number of page missing exceptions during startup, and also leads to a longer startup time. That’s where the binary rearrangement comes in -
Principle of binary rearrangement
Above, it analyses the reasons of the binary rearrangement, is the waste should be page space did not make full use of the space of each page page fault caused by abnormal rise in the Numbers, the principle of binary rearrangement is actually used will start the phase method, function, all rows in the front, so it can make full use of the space of each page, at the same time also reduced the number of page fault anomalies. As shown in the figure below:
Significantly reduced more than half of the abnormal number of missing pages -
Binary rearrangement practices
By analyzing the principles above, we can know that if we do binary rearrangement, we only need to change the order of compile time methods, functions, etc. The essence of this is to rearrange the symbols of the startup load.
- Method of modifying the order of the order
Xcode
Is using a linker calledld
.ld
There’s a parameter calledOrder File
, we can configure one with this parameterorder
File path.
We can go through theBuild Settings
->Order File
Configure a suffix asorder
File path of. In thisorder
When the project is compiled, it will be loaded in accordance with the order of the files to achieve our optimization, so the key point of binary rearrangement isOrder File
File generation - To obtain
Order File
File method- If the project is not large, you can also find the method and function to run in the startup phase according to the project, and write it by yourself
Order File
File. hook
objc_msgSend
, but becauseobjc_msgSend
The parameter of is variable and needs to be obtained through assembly, so the threshold to use is relatively high. And they only get OC and@ objc swift
Methods after- Static scan: scanning
Mach-O
Symbol and function data stored in specific segments and sections Clang
Piling: namely, batch hook, which can achieve 100% symbol coverage, that is, complete acquisitionSwift, OC, C, block
function
- If the project is not large, you can also find the method and function to run in the startup phase according to the project, and write it by yourself
Clang
Insert the pilellvm
A simple code coverage check is built in (SanitizerCoverage
). It inserts calls to user-defined functions at the function level, basic block level, and edge level,The corresponding document
Specific steps:- Open the configuration
SanitizerCoverage
In thebuild setting
In the searchOther C Flags
, the following figureAdd if it is an OC project-fsanitize-coverage=func,trace-pc-guard
, if it is a Swift project-sanitize-coverage=func
and-sanitize=undefined
- add
hook
methodsvoid __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { static uint64_t N; // Counter for the guards. if (start == stop || *start) return; // Initialize only once. printf("INIT: %p %p\n", start, stop); for (uint32_t *x = start; x < stop; x++) *x = ++N; // Guards should start from 1.} void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {// Guard is a sentinel, This is where the load method is filtered out, so we need to comment out if (!). *guard) return; Void *PC = __builtin_return_address(0); void *PC = __builtin_return_address(0); void *PC = __builtin_return_address(0); char PcDescr[1024]; printf("guard: %p %x PC %s\n", guard, *guard, PcDescr); }Copy the code
The main way to do this is
__sanitizer_cov_trace_pc_guard
Method, here we can get the address of the corresponding method, why is the method called before it executes__sanitizer_cov_trace_pc_guard
For methods, you can use breakpoint debugging to see how to debug a method or function, and then look at the assembly code as shown below:Found inserted before method execution__sanitizer_cov_trace_pc_guard
Method, all function execution is restricted__sanitizer_cov_trace_pc_guard
Method, and put a breakpoint in front of the blockblock
It will also be inserted before execution__sanitizer_cov_trace_pc_guard
Method, continue to viewswift-oc
Mix isswift
Is the method going to behook
Will also behook
Therefore, it is also verified that clang piling method can cover all methods and functions. - Get symbol above
hook
Method we know that we can get the address of the current method or function, and once we get the address we can go throughdladdr
Method to remove the corresponding method or function information specific code as shown in the figure below:founddli_sname
That’s what we want, and what we’re going to do is we’re going to store those symbols and we’re going to generate an order and then we’re going to configure thatOrder file
It’s done. - Output order file
Now that you have the symbols, the final task is to output the order file.
Specific ideas: We can do it at__sanitizer_cov_trace_pc_guard
Store the function address information and giveapp
Add a tap on the screen listener event, and wait until the first screen is loaded to show that the startup is complete and all the required methods are loaded. At this point, we iterate over the address information in this method and output the symbol.
I am here to borrow the linked list storage, so first to create a node as shown in the figure below:And then throughOSQueueHead
Atomic queues are created to ensure read and write security.
throughOSAtomicEnqueue
Method will benode
In the queue, through the listnext
The pointer accesses the next symbolThe next step is to read and write the order file: the code is as follows-(void)touches began :(NSSet< touches *> *)touches withEvent:(UIEvent *)event {// create array NSMutableArray<NSString *> * symbolNames = [NSMutableArray array]; While (YES) {// loop once! Will also be HOOK up once!! SYNode * node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next)); if (node == NULL) { break; } Dl_info info = {0}; dladdr(node->pc, &info); // printf("%s \n",info.dli_sname); NSString * name = @(info.dli_sname); free(node); BOOL isObjc = [name hasPrefix: @ "+ ["] | | [name hasPrefix: @" - ["]; / / need to pay attention to if not OC method need to add the underline nsstrings * symbolName = isObjc? name : [@ "_" stringByAppendingString: name], [symbolNames addObject: symbolName];} / / reverse array enumerator NSEnumerator * = NSMutableArray * funcs = [NSMutableArray] ArrayWithCapacity: symbolNames. Count]; nsstrings * name; / / to heavy! While (name = [enumerator nextObject]) {if (! [funcs ContainsObject :name]) {funcs addObject:name;}} [funcs removeObject:[NSString StringWithFormat: @ "% s", __FUNCTION__]]; / / an array into a string nsstrings * funcStr = [funcs componentsJoinedByString: @ "\ n"); / / the string written to the file / / file path nsstrings * filePath = [NSTemporaryDirectory () stringByAppendingPathComponent: @ "tudou. Order"); / / the file content NSData * fileContents = [funcStr dataUsingEncoding:NSUTF8StringEncoding]; [[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil]; }Copy the code
The run completes the discovery and generates the order file
- The Xcode configuration order file is shown in the configuration file
- To view the binary rearrangement result, you can also view the generated link map file:
Before binary rearrangement:The discovery is done in file by method order.
After binary rearrangement:
It turns out that this is in the order of our order file
- Open the configuration
- Method of modifying the order of the order