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 threadkeyBinding: such as a destructor for thread data
  • static_init
    • Call the global static C++ constructor
  • runtime_init
    • Runtime Initializes the runtime environment
  • exception_init
    • Initialize thelibobjcException handling system
  • 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 theEdit SchemeRunArgumentAdding environment variablesOBJC_PRINT_LOAD_METHODS = YESCan print all+loadmethods

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 staticC++ The constructor
  • libc indyldcallThe constructorBefore 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 toA 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.