Hi 👋
- Wechat: RyukieW
- 📦 Archive of technical articles
- 🐙 making
My personal project | Minesweeper Elic Endless Ladder | Dream of books |
---|---|---|
type | The game | financial |
AppStore | Elic | Umemi |
This article is based on dyLD-832.7.3 and objC4-818.2 source code
- Series of articles:
- [iOS app startup (I)] Dyld and main function
- [iOS App Startup (2)] Environment configuration and Runtime initialization
- [iOS app Startup (3)] Image file reading and loading
- The process of realizing and initialize a Class is illustrated
- [iOS app launch (4)] Classified loading
preface
Combining dyld with main and symbolic breakpoints we find the call stack of _objc_init
- dyld`ImageLoaderMachO::doModInitFunctions
- libSystem.B.dylib`libSystem_initializer
- libdispatch.dylib`libdispatch_init
- libdispatch.dylib`_os_object_init
- libobjc.A.dylib`_objc_init
What does _objc_init do
- Boot initialization to register images using dyLD.
- Called by libSystem before library initialization time
/*********************************************************************** * _objc_init * Bootstrap initialization. Registers our image notifier with dyld. * Called by libSystem BEFORE library initialization time * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
Copy the code
1.1 Source Code Analysis
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
/// initialize environment variables
environ_init(a);/// bindings for thread 'keys' : such as thread data destructors
tls_init(a);/// call the global static C++ constructor
static_init(a);/// the runtime environment is initialized
runtime_init(a);// initialize the exception handling system for 'libobjc'
exception_init(a);#if __OBJC2__
// the cache condition is initialized
cache_t: :init(a);#endif
// start the callback mechanism
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
Copy the code
Here are some important steps:
- environ_init
- Initialize environment variables
- tls_init
- About the thread
key
Binding: such as a destructor for thread data
- About the thread
- static_init
- Call the global static C++ constructor
- runtime_init
- Runtime Initializes the runtime environment
- exception_init
- Initialize the
libobjc
Exception handling system
- Initialize the
- cache_t::init()
- The cache condition is initialized
- _imp_implementationWithBlock_init
- Start the callback mechanism
- _dyld_objc_notify_register *
Second, the environ_init
Initialize environment variables
- Modifying environment variables is a great way to help with development debugging
Modify environment variables for debugging
For example, in a scenario where there is a lot of project code and a lot of SDK references, what should I do if I want to know which classes implement the +load method?
- Modify an environment variable and print it
- Open the
Edit Scheme
–Run
–Argument
Adding environment variablesOBJC_PRINT_LOAD_METHODS = YES
Can print all+load
methods
- Open the
Effect:
objc[58337]: LOAD: category 'NSObject(NSObject)' scheduled for +load
objc[58337]: LOAD: +[NSObject(NSObject) load]
objc[58337]: LOAD: category 'NSObject(NSObject)' scheduled for +load
objc[58337]: LOAD: +[NSObject(NSObject) load]
objc[58337]: LOAD: class 'NSColor' scheduled for +load
objc[58337] : LOAD: class 'NSApplication' scheduled for +load
objc[58337] : LOAD: class 'NSBinder' scheduled for +load
objc[58337] : LOAD: class 'NSColorSpaceColor' scheduled for +load
objc[58337] : LOAD: class 'NSNextStepFrame' scheduled for +load
objc[58337] : LOAD: +[NSColor load]
objc[58337]: LOAD: +[NSApplication load]
objc[58337]: LOAD: +[NSBinder load]
objc[58337]: LOAD: +[NSColorSpaceColor load]
objc[58337]: LOAD: +[NSNextStepFrame load]
objc[58337]: LOAD: category 'NSError(FPAdditions)' scheduled for +load
objc[58337]: LOAD: +[NSError(FPAdditions) load]
objc[58337]: LOAD: class '_DKEventQuery' scheduled for +load
objc[58337] : LOAD: +[_DKEventQuery load]
objc[58337]: LOAD: class 'RYModel' scheduled for +load
objc[58337] : LOAD: +[RYModel load]
Copy the code
The custom model implements the +load method printed out, +[RYModel load].
Gets available environment variables
Enter the command export OBJC_HELP=1 in any terminal to obtain the list of environment variables, choose your own need to use, can improve the efficiency of development and debugging oh.
✗ ➜ RyukieDevGitBook git: (master)export OBJC_HELP=1
objc[57485]: Objective-C runtime debugging. Set variable=YES to enable.
objc[57485]: OBJC_HELP: describe available environment variables
objc[57485]: OBJC_PRINT_OPTIONS: list which options are set
objc[57485]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
...
Copy the code
Third, static_init
Call the global static C++ constructor
- Perform global static
C++
The constructor
libc
indyld
callThe constructor
Before the call_objc_init()
- So we have to deal with it ourselves
/*********************************************************************** * static_init * Run C++ static constructor functions. * libc calls _objc_init() before dyld would call our static constructors, * so we have to do it ourselves. **********************************************************************/
static void static_init(a)
{
size_t count;
auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
inits[i]();
}
auto offsets = getLibobjcInitializerOffsets(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
UnsignedInitializer init(offsets[i]);
init();
}
}
Copy the code
3.1 validation
Add a C++ constructor to my code
__attribute__((constructor)) void ryFunc(a) {
printf("My constructor: %s \n", __func__);
}
Copy the code
- I noticed that I didn’t execute my C++ constructor in this step
3.2 thinking
Instead of calling all the C++ constructors, you are calling the constructors in the underlying objc library.
Add a constructor to objc’s source code:
__attribute__((constructor)) void ryFuncInObjc(a) {
printf("My constructor: %s \n", __func__);
}
Copy the code
Normally, no log is generated
Logs are output here
My constructor: ryFuncInObjcCopy the code
3.3 summarize
- The C++ constructor called here specifically refers to
A set of constructors defined in objC's source code
- Because the global constructor is so important, to ensure that the global constructor call is timely, it is called here itself.
Four, runtime_init
Runtime Initializes the runtime environment
void runtime_init(void)
{
objc::unattachedCategories.init(32);
objc::allocatedClasses.init(a); }Copy the code
So by looking at these two init methods here, we see that these are collection types
public:
template <typename. Ts>void init(Ts &&... Args) {
new (_storage) Type(std::forward<Ts>(Args)...) ; }Type &get(a) {
return *reinterpret_cast<Type *>(_storage); }};Copy the code
4.1 unattachedCategories
static UnattachedCategories unattachedCategories;
} // namespace objc
Copy the code
4.2 allocatedClasses
A table that stores all of the classes and metaclasses that have been allocated
/*********************************************************************** * allocatedClasses * A table of all classes (and metaclasses) which have been allocated * with objc_allocateClassPair. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
namespace objc {
static ExplicitInitDenseSet<Class> allocatedClasses;
}
Copy the code
Fifth, exception_init
Initialize libobJC’s exception handling system
/*********************************************************************** * exception_init * Initialize libobjc's exception handling system. * Called by map_images(). * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
Copy the code
- You can also customize an exception catch callback if necessary
FOUNDATION_EXPORT void NSSetUncaughtExceptionHandler(NSUncaughtExceptionHandler * _Nullable);
Copy the code
Six, _imp_implementationWithBlock_init
Start the callback mechanism. Usually nothing is done because all initializations are lazily loaded, but for some processes trampolines dylib can’t wait to be loaded.
Seven, _dyld_objc_notify_register
Here, the image file is read and loaded. Go to “iOS App Startup (3)” to learn more about it.