Preface:

In the last article, we detailed the process of iOS Category. We mentioned that dyLD interprets the Mach -O file at compile time in the runtime library entry function _objc_init corresponding to three different time corresponding callback functions.

The load method is called when the dyLD load is finished, which is also when the load method is called. For those interested, read the last blog post.

Without further discussion, also open objC4 source.

To explore the process

1. Runtime library entry function

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();
    lock_init();
    exception_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
Copy the code

Map_images, load_images, and unmap_image are three functions that I mentioned in my last blog post, so I won’t go into details here. Just go load_images:

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    // Return without taking locks if there are no +load methods here.
    if(! hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        mutex_locker_t lock2(runtimeLock);
        / * * * /
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}
Copy the code

This code is divided into two steps, first prepare, then call. –> GO

2. Prepare the load method

void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertLocked();

    Load the list of classes from the Macho file
    classref_t *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        // Array: [< CLS,method>,< CLS,method>,< CLS,method>
        schedule_class_load(remapClass(classlist[i]));
    }

    // For the classification of operations!
    category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = categorylist[i];
        Class cls = remapClass(cat->cls);
        if(! cls)continue;  // category for ignored weak-linked classrealizeClass(cls); assert(cls->ISA()->isRealized()); add_category_to_loadable_list(cat); }}Copy the code

There are two more steps here

The load method of class 2.1 reads and sorts

// recursive call
static void schedule_class_load(Class cls)
{
    if(! cls)return;
    assert(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;

    // Ensure superclass-first ordering
    schedule_class_load(cls->superclass);
    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}
Copy the code

In fact, it reads a list of all the classes from Mach-O

  • Recursively according to the current classsuperclassThe Pointers are arranged to satisfy a rule:

The load method of a class must be called after the load method of its parent.

  • When the recursionNSObjectAnd then find the parent class asnil, out of recursion. End the permutation, and place eachloadmethodsclsmethodStored globallyloadable_classIn the structure.

2.2 Load method of classification reading and sorting

The general load process of a category is similar to that of a class. The list of categories is read from Mach-O and then iterated directly, adding the CLS and method of the load method of the category to the global loadable_categories structure.

That is, the order in which the load methods are loaded between different classes depends on the order in which the Mach-O file was compiled.

3. The load calls

call_load_methods :

void call_load_methods(void)
{
    static bool loading = NO;
    bool more_categories;

    loadMethodLock.assertLocked();

    // Re-entrant calls do nothing; the outermost call will finish the job.
    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();

    do {
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            // Call the load method of the class
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}
Copy the code

Here it’s a little bit easier. Start by going through the list of load methods of the previously stored class. Then iterate through the list of categories and call them in turn.

Conclusion:

  • loadMethods in theruntimeLibrary start runtime call.
  • Of a classloadMethod in all parent classesloadAfter the method call.
  • Classification ofloadMethod in the classloadMethod.
  • Between different categoriesloadThe method call order is related to the compile order.