Good good study, not impatient, progress a little bit every day;
preface
This article will explain the iOS application loading process from the basic principles.
Program loading framework
Source files are precompiled to analyze the code lexicon and syntax, and then handed to the compiler. After compiling, some assembler files are generated, links are loaded into the application, and finally become executable files.
Dynamic/static libraries
Static library: link, will be a complete copy to the executable file, will be used by the system for many times, copy many copies;
- Static library form:.a and.framework form
* Dynamic library: link does not copy, the program is dynamically loaded into memory by the system when it runs, the system only loads once, multiple programs share, saving memory space;
- Dynamic library forms:.so,.tbd(formerly called
.dylib
) and the framework
The difference between static library and dynamic library is mainly in the link, one is static link, one is dynamic link;
After the static/dynamic library generates the executable file, then verify the executable file (note: try to use MacO project verification, if you use iOS project, when the terminal runs the executable file, the simulator or the real machine will run, at this time will need some access rights, processing is more troublesome; I use source engineering validation here.
Run the source code project to generate executable files:
In Finder, drag the executable into the terminal and run it;
Advantages of using static or dynamic libraries:
- 1. Reduce the package size;
- 2. Dynamic library hot update (Currently Apple has eliminated this method)
- 3. Improve the efficiency of program operation
Now that you understand some of the principles of libraries, how do you load them into memory in your application?
dyld
-
Dyld linker
Libraries are loaded into memory through the linker, the DYLD dynamic linker;
When the App starts, it will load the library required by the application (e.g. LibSystem library), register the callback function (_dyLD_OBJC_notify_register), and load a new image. After image is loaded, map_images and Load_images are executed. Main () is executed only after these two functions are executed. This is the library loading flow;
-
Dyld principle
Next, we’ll focus on the dyld linking process before calling main().
To run the project, run the start process before the main function.
What does the start process look like with global breakpoints?
The start breakpoint is not executed, but we see that the log area prints the load method log before main() is called, which means that the load method was called before main().
The breakpoint is in the load function, and the stack queue is printed in the log area via BT:
As you can see from the stack flow, _DYLD_START is executed first in the dyLD source code.
-
Download dyLD source code, you can download through apple open source network, the latest official website is DYLD-852.2 version, today also use this version explanation;
-
Dyld source code analysis
In the dyLD source code, the _dyLD_START function is found in assembly form, and according to the different architecture, using different processes;
The i386 architecture
X86 architecture
The arm architecture
Dyldbootstrap ::start(app_MH, argc, argv, dyLD_MH, &startGlue) So we can go directly to this function;
In the dyLDBootstrap file, execute the start function. So, we first query the dyLDBootstrap file, then query the start function;
Dyld ::_main () = dyld::_main () = dyld::_main () = dyld::_main ()
The result returned by the dyld::_main function is more likely to be assigned by the sMainExecutable function.
sMainExecutable
Initialization:
// instantiate ImageLoader for main executable
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
/ / instantiateFromLoadedImage: image file loader
Copy the code
Load the image file and assign it to sMainExecutable;
loadInsertedDylib
Get and insert the dynamic library:
// load any inserted libraries
**if** ( sEnv.DYLD_INSERT_LIBRARIES ! = * *NULL* * * *) {for(* * * *const* * * *char* * * * *const*** lib = sEnv.DYLD_INSERT_LIBRARIES; *lib ! = * *NULL* *; ++lib)loadInsertedDylib(*lib);
}
// record count of inserted libraries so that a flat search will look at
// inserted libraries, then main, then others.
// Get the number of dynamic library image files
sInsertedDylibCount = sAllImages.size(a)- 1;
Copy the code
After obtaining the image file, start linking;
link
Link image file:
// Start linking image files
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, **true**, ImageLoader::RPathChain(* *NULL* *, * *NULL* *),- 1);
sMainExecutable->setNeverUnloadRecursive(a);if ( sMainExecutable->forceFlat() ) {
gLinkContext.bindFlat = **true* *; gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding; }// Check whether the dynamic library image file exists
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
//link dynamic library image file
link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL.NULL), - 1);
image->setNeverUnloadRecursive(a); }...Copy the code
Image file link ends, start binding program;
weakBind
Weak reference binder:
sMainExecutable->weakBind(gLinkContext);
gLinkContext.linkingMainExecutable = false; .Copy the code
When link, weakBind ends, the main program starts to run;
initializeMainExecutable
: Runs the main program
initializeMainExecutable(a);Copy the code
Finally, tell dyld to enter the main() function
notifyMonitoringDyldMain
: Notifies DYld that the main program can be accessed
notifyMonitoringDyldMain(a);Copy the code
This is the general process of DYLD, there are a lot of details, you can see by yourself, I will not elaborate on them here;
The initializeMainExecutable program runs
initializeMainExecutable
Source:
As you can see in the flow section, there is some preparation in initializing the main application system. What is the preparation? Next we can look at processInitializers();
processInitializers()
Function:
void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread,
InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images)
{
uint32_t maxImageCount = context.imageCount() +2;
ImageLoader::UninitedUpwards upsBuffer[maxImageCount];
ImageLoader::UninitedUpwards& ups = upsBuffer[0];
ups.count = 0;
// Calling recursive init on all images in images list, building a new list of
// uninitialized upward dependencies.
// In the current thread, call recursive init on all images in the images list to create a new images list
for (uintptr_t i=0; i < images.count; ++i) {
images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups);
}
// If any upward dependencies remain, init them.
// Recursive process
if ( ups.count > 0 )
processInitializers(context, thisThread, timingInfo, ups);
}
Copy the code
ProcessInitializers () is a recursive process that inits all of your images. What if you call recursiveInitialization? The process is really, really confusing
Following the
To continue
see
!
recursiveInitialization
function
Load the dependent file first, and then load the current file. This is because the current file cannot be loaded without the dependency file being loaded; For example: ViewA has a child ViewB, if the child ViewB is not loaded, then ViewA can not reference ViewB, resulting in ViewA can not complete;
In the recursiveInitialization function, notifySingle is executed after loading the file.
notifySingle
function
sNotifyObjCInit
initializeMainExecutable
Flow chart: