preface

During dyLD loading, we know that the _objc_init method is called, so what exactly is going on in the _objc_init method? So let’s explore.

_objc_init method

_objc_init method implementation

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    environ_init();
    tls_init();
    static_init();
    runtime_init();
    exception_init();
    cache_init();
    _imp_implementationWithBlock_init();
    
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}
Copy the code

What does this method mainly do from the _objc_init implementation

environ_init()

DYLD_PRINT_STATISTICS = YES Specifies the time between main() and APP startup. For an introduction to environ_init(), see the section on nviron_init() in the blog iOS- Underlying Principles 16: DyLD and ObjC

tls_init()

Mainly used for binding on thread keys, such as per-thread data destructors.

void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
    pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
    _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
Copy the code

static_init()

Mainly C++ static constructors

static void static_init() { size_t count; auto inits = getLibobjcInitializers(&_mh_dylib_header, &count); for (size_t i = 0; i < count; i++) { inits[i](); }}Copy the code

runtime_init()

This is mainly run-time initialization, which is divided into two parts: class initialization and table initialization of classes

void runtime_init(void)
{
    objc::unattachedCategories.init(32);
    objc::allocatedClasses.init();
}
Copy the code

exception_init()

Initialize libobJC exception handling

/***********************************************************************
* 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

cache_init()

This is mainly cache initialization

void cache_init()
{
#if HAVE_TASK_RESTARTABLE_RANGES
    mach_msg_type_number_t count = 0;
    kern_return_t kr;

    while (objc_restartableRanges[count].location) {
        count++;
    }

    kr = task_restartable_ranges_register(mach_task_self(),
                                          objc_restartableRanges, count);
    if (kr == KERN_SUCCESS) return;
    _objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)",
                kr, mach_error_string(kr));
#endif // HAVE_TASK_RESTARTABLE_RANGES
}
Copy the code

_imp_implementationWithBlock_init()

It is mainly used to initiate mechanism callbacks

/// everything is initialized lazily, but for certain processes we eagerly load
/// the trampolines dylib.
void
_imp_implementationWithBlock_init(void)
{
#if TARGET_OS_OSX
    // Eagerly load libobjc-trampolines.dylib in certain processes. Some
    // programs (most notably QtWebEngineProcess used by older versions of
    // embedded Chromium) enable a highly restrictive sandbox profile which
    // blocks access to that dylib. If anything calls
    // imp_implementationWithBlock (as AppKit has started doing) then we'll
    // crash trying to load it. Loading it here sets it up before the sandbox
    // profile is enabled and blocks it.
    //
    // This fixes EA Origin (rdar://problem/50813789)
    // and Steam (rdar://problem/55286131)
    if (__progname &&
        (strcmp(__progname, "QtWebEngineProcess") == 0 ||
         strcmp(__progname, "Steam Helper") == 0)) {
        Trampolines.Initialize();
    }
#endif
}
Copy the code

Dyld is associated with objC

_dyld_objc_notify_register(&map_images, load_images, unmap_image)

Mainly dyLD registration of the actual code implementation

void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped)
{
    dyld::registerObjCNotifiers(mapped, init, unmapped);
}
Copy the code

It can be seen from the middle above

  • mappednamelymap_images
  • initnamelyload_images
  • unmappednamelyunmap_image

Map_images () function analysis

/*********************************************************************** * map_images * Process the given images which are being mapped in by dyld. * Calls ABI-agnostic code after taking ABI-specific locks. * * Locking: write-locks runtimeLock **********************************************************************/ void map_images(unsigned  count, const char * const paths[], const struct mach_header * const mhdrs[]) { mutex_locker_t lock(runtimeLock); return map_images_nolock(count, paths, mhdrs); }Copy the code

The map_images_NOLock function is important, so let’s go to the map_images_NOLock function

map_images_nolock

Let’s look at the code implementationWe can see that in the screenshot_read_imagesThat’s what we’re going to focus on

_read_images function analysis

Whether it is the first load

Fix precompile time@selectorThe disorder problem of

Error class handling, passedreadClassRead information about the outgoing class

Example Reset the mirror mapping

Message processing

Read the protocol, if any, in the class

Mapping protocol

Load classification

Pay attention toIn the classification processing is mainly throughload_categories_nolockProcessing, we’re inload_categories_nolockIn the function

load_categories_nolockfunction

static void load_categories_nolock(header_info *hi) {
    bool hasClassProperties = hi->info()->hasCategoryClassProperties();

    size_t count;
    auto processCatlist = [&](category_t * const *catlist) {
        for (unsigned i = 0; i < count; i++) {
            category_t *cat = catlist[i];
            Class cls = remapClass(cat->cls);
            locstamped_category_t lc{cat, hi};

            if (!cls) {
                // Category's target class is missing (probably weak-linked).
                // Ignore the category.
                if (PrintConnecting) {
                    _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
                                 "missing weak-linked target class",
                                 cat->name, cat);
                }
                continue;
            }

            // Process this category.
            if (cls->isStubClass()) {
                // Stub classes are never realized. Stub classes
                // don't know their metaclass until they're
                // initialized, so we have to add categories with
                // class methods or properties to the stub itself.
                // methodizeClass() will find them and add them to
                // the metaclass as appropriate.
                if (cat->instanceMethods ||
                    cat->protocols ||
                    cat->instanceProperties ||
                    cat->classMethods ||
                    cat->protocols ||
                    (hasClassProperties && cat->_classProperties))
                {
                    objc::unattachedCategories.addForClass(lc, cls);
                }
            } else {
                // First, register the category with its target class.
                // Then, rebuild the class's method lists (etc) if
                // the class is realized.
                if (cat->instanceMethods ||  cat->protocols
                    ||  cat->instanceProperties)
                {
                    if (cls->isRealized()) {
                        attachCategories(cls, &lc, 1, ATTACH_EXISTING);
                    } else {
                        objc::unattachedCategories.addForClass(lc, cls);
                    }
                }

                if (cat->classMethods  ||  cat->protocols
                    ||  (hasClassProperties && cat->_classProperties))
                {
                    if (cls->ISA()->isRealized()) {
                        attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
                    } else {
                        objc::unattachedCategories.addForClass(lc, cls->ISA());
                    }
                }
            }
        }
    };

    processCatlist(_getObjc2CategoryList(hi, &count));
    processCatlist(_getObjc2CategoryList2(hi, &count));
}
Copy the code

From the load_categories_NOLock function implementation, you can see that this function links classes, instance methods, protocols, attributes, and class methods again.

Non-lazy-loaded class processing

Handle unused classes

Dyld and OBJC association summary

  • dyld_startcall_objc_initTo initialize,_objc_initThrough thedyldcall_dyld_objc_notify_registerFunction, passed inmap_imageswithload_imagesThese two parameters to handle
  • map_imagesthroughmap_images_nolockA function call_read_imagesfunction
  • in_read_imagesProcessing in functionClass information, attributes, protocols, classifications, etc
  • When everything is ready, go back againdyld_startAt this time,dyldwithobjcRelate to