I'm the preface.
The function of an iOS app is main()
located main.m
in, which is the program portal we know well. But knowing more about OBJC found that the program main
has executed a lot of code before entering our functions, such as familiar + load
methods. This article will follow the sequence of execution of the program, inquiring into, and dyld
runtime
seeing what happens before the main function.
Start a dynamic link library from Dyld
All of the system frameworks used in iOS are dynamically linked, analogous to plugs and inserts, statically linked code is plugged in and plugged in after a static link, and the runtime executes the binaries directly, while the dynamic link needs to complete the "plug-in" process when the program starts, So before we write the code, the dynamic connector needs to do the preparation.
This is the link list that you see in Xcode:
These frameworks will be loaded during the dynamic linking process, and there is also an implicit link framework that can be tested: First find the executable file, I called TestMain
the project, the emulator path found TestMain.app
, the executable file by default, and then through the otool
command:
$ otool-l Testmain
Testmain: /system/library/frameworks/coregraphics.framework/coregraphics /system/library/frameworks/ Uikit.framework/uikit /system/library/frameworks/foundation.framework/foundation /System/Library/ Frameworks/corefoundation.framework/corefoundation /USR/LIB/LIBOBJC. A.dylib /usr/lib/libsystem.dylib
The-l parameter prints out the framework of all link (version information is removed):
In addition to the more CoreGraphics
(by Uikit dependency), there are two default added Lib. LIBOBJC, OBJC and Runtime,libsystem, contain a number of system-level LIB, listed in several well-known: Libdispatch (GCD), Libsystem_c (C-language library), Libsystem_blocks (Block), Libcommoncrypto (commonly used MD5 functions) and so on. These lib are dylib
formats (such as DLLs in Windows), and the system uses dynamic links for several advantages:
- Code sharing: Many programs dynamically link these lib, but they have only one copy in memory and disk
- Easy maintenance: Because the LIB is dependent on the implementation of the program is only link, so these lib is easy to do updates, such as
libSystem.dylib
libSystem.B.dylib
the alias, which day to upgrade directly libSystem.C.dylib
and then replace the avatar on the line
- Reduce the volume of executable files: executable files are much smaller than static links
Dyld
dyld
-The dynamic Link editor (this abbreviation corresponds to very strange, I feel is dynamic Linker daemon? Apple's dynamic linker, the system kernel the initial preparation of the start-up program, to Dyld responsible, invoke and translate "Mikeask this blog" on the Dyld of the role of the Order of the summary:
- Boot and start yourself from the original call stack left by kernel
- Load the dynamic link library that the program relies on
递归
into memory, of course there are缓存机制
- The Non-lazy symbol immediately link to the executable file, the Lazy memory table
- Runs static initializers for the executable
- Locate the main function of the executable file, prepare the parameters and invoke the
- It is responsible for binding the lazy symbol in program execution, providing runtime dynamic loading services, providing debugger interface
- Program main function return after executing static terminator
- The _exit function of Libsystem after the end of main function in some scenes
Thanks to the Dyld is open source, GitHub address, we can from the source of a probe.
It all stems from dyldStartup.s
this document, in which the Assembly implements the method named, the __dyld_start
assembly is too jerky, it mainly does two things:
- Calling
dyldbootstrap::start()
methods (omitting parameters)
- The previous method returns the main function address, fills in the parameters and calls the main function
This step can be readily verified, setting a 符号断点
break in _objc_init
:
This function is runtime
the initialization function, which is mentioned later. The program runs at a very early time and looks at the call stack:
See the bottom of the dyldbootstrap::start()
method, and then called the dyld::_main()
method, which completed the recursive loading of the dynamic library process just mentioned, due libSystem
to the default introduction, the stack appeared in libSystem_initializer
the initialization method.
Imageloader
Of course, this image is not the meaning of the picture, it probably represents a binary file (executable or so file), which is compiled symbols, code, etc., so the ImageLoader
role is to load these files into memory, and each file corresponding to a Imageloader instance to load.
Two-Step walk:
- When the program runs, it loads the dynamically linked image recursively (that is, the time of a string of recursive calls in the test stack above)
- Load all symbols recursively from the executable image
Of course all this happens before our real main function is executed.
Runtime and +load
Just talked about libSystem
is a set of several system lib, so it is just a container lib, and it is also open source, inside essentially a file, init.c, details do not say, by the libSystem_initializer
gradual call to the _objc_init
, This is the initial entry for the OBJC and runtime.
In addition to the initialization of the runtime environment, the _objc_init
new image is bound to the callback after it is loaded:
Dyld_register_image_state_change_handler (Dyld_image_state_bound, 1/*batch*/, &map_images);d Yld_register _image_state_change_handler (dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images);
It is visible that Dyld serves as the coordinator of the runtime
ImageLoader
middle, and when the new image is loaded, it is left to the runtime chef to parse the binary file's symbol table and code. Continue with the above breakpoint method, break the mysterious +load
function:
See the entire call stack and order clearly:
- Dyld starts the program binary file initialization
- By Imageloader read the image, which contains our classes, methods and other symbols
- Since runtime binds a callback to Dyld, Dyld notifies runtime to process when the image is loaded into memory
- Runtime takes over and calls Map_images for parsing and processing, then calls the Call_load_methods method in Load_images, loops through all the loaded classes, Call the class's Load method and its category's Load method by inheritance level
At this point, all the symbols in the executable file and in the dynamic library (Class,protocol,selector,imp, ... ) have been successfully loaded into memory in the format, managed by the runtime, and after that, the runtime's methods (Dynamic Add class, method mix, etc. to take effect)
Several QA on the Load method
Q: Do I need to reload my class's Load method without needing to tune the parent?
A:runtime is responsible for recursive invocation in the order of inheritance, so we can't tune super
Q: Can I replace the method implementation of a class in the system framework (such as Uikit) in the load method of your class?
A: Yes, because all of the library-dependent classes are loaded before their classes, because of the dynamic linking process
Q: Do I need to manually add @autoreleasepool when loading load?
A: No, it was added before and after the runtime called the Load method objc_autoreleasePoolPush()
objc_autoreleasePoolPop()
.
Q: Do you want the load method of a class to be called to import the file somewhere?
A: No, as long as the symbol of this class is compiled into the final executable, the Load method is called (The Reveal SDK uses this to work as soon as it is introduced into the project)
Brief summary
The entire event is led by Dyld, and after the initialization of the running environment is completed, the binary files are loaded into memory with Imageloader in a format.
The dynamic link relies on the library, and the runtime is responsible for loading the structure into the OBJC definition, and after all initialization work is done, Dyld invokes the true main function.
It is worth noting that this process is far more complex than written, here only refers to the runtime this branch, there are like GCD
, and XPC
so on the system library initialization branch did not mention (of course, there is a caching mechanism, they do not kill the initialization), summed up is the main function before the execution, The system did a lot of loading and initialization work, but are very well hidden, we do not care.
The main function of loneliness
When this is all over, Dyld will clean up the scene and return the call stack, leaving only:
The lonely main function, which appears to be the beginning of a program, is indeed a wonderful end.
What happened before the iOS program main function