When the load method is called

Before the main function, the dyLD program loads and initializes classes/classes as it loads them into memory, and when it’s done, the load method is called

The order in which the load methods are called

  1. All classes (call the load function of the class in compile order, but call the load function of all the parent classes of the inherited chain before calling the load function of this class)
  2. All classes (call the load function of the class in compile order)

The core structure

struct loadable_class {
    Class cls;  // may be nil
    IMP method;
};

struct loadable_category {
    Category cat;  // may be nil
    IMP method;
};
Copy the code

Load method call order principle

  1. Put all the classes that implement the load method into a listloadable_classesIs stored in this listloadable_class, which are processed in the same order as the compile order, but are added recursively to all classes in the class inheritance chain before being added to the classloadable_classesIn, all classes are not added repeatedly
  2. It’s all implementedloadMethods, into a listloadable_categoriesThe list is stored inloadable_category
  3. According to theloadable_classes, traversing from front to back, calling the class’s load method one by one according to the function address
  4. According to theloadable_categories, traversing from front to back, calling the classified load method one by one according to the function address

Considerations for the load method call

  1. Call directly according to the function address of the load method, there is no outbound message sending mechanism
  2. Meta_class has an instance of flags,&RW_LOADEDYou can determine if you want to joinloadable_classes, you can avoid repeated joining
  3. According to the compilation product machO__DATAPart of the__objc_nlclslistPartially, you can get all the implementationsloadMethods the class

demo


#import <Foundation/Foundation.h>

@interface WYPerson: NSObject
@end
@implementation WYPerson
+ (void)load { NSLog(@"WYPerson load"); };
@end

@interface WYPerson(Eat)
@end
@implementation WYPerson(Eat)
+ (void)load { NSLog(@"WYPerson + Eat load"); };
@end

@interface WYPerson(Run)
@end
@implementation WYPerson(Run)
+ (void)load { NSLog(@"WYPerson + Run load"); };
@end

@interface WYStudent: WYPerson
@end
@implementation WYStudent
+ (void)load { NSLog(@"WYStudent load"); };
@end

@interface WYStudent(Eat)
@end
@implementation WYStudent(Eat)
+ (void)load { NSLog(@"WYStudent + Eat load"); };
@end

@interface WYStudent(Run)
@end
@implementation WYStudent(Run)
+ (void)load { NSLog(@"WYStudent + Run load"); };
@end

@interface WYAnimal: NSObject
@end
@implementation WYAnimal
+ (void)load { NSLog(@"WYAnimal load"); };
@end

@interface WYAnimal(Eat)
@end
@implementation WYAnimal(Eat)
+ (void)load { NSLog(@"WYAnimal + Eat load"); };
@end

@interface WYAnimal(Run)
@end
@implementation WYAnimal(Run)
+ (void)load { NSLog(@"WYAnimal + Run load"); };
@end

int main(int argc, const char * argv[]) {
    return 0;
}
Copy the code

The source code

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

    runtimeLock.assertLocked(a);classref_t const *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i]));
    }

    category_t * const *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 class
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class extensions and categories on Swift "
                        "classes are not allowed to have +load methods");
        }
        realizeClassWithoutSwift(cls, nil);
        ASSERT(cls->ISA() - >isRealized());
        add_category_to_loadable_list(cat); }}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->getSuperclass());

    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}

/*********************************************************************** * add_class_to_loadable_list * Class cls has just become connected. Schedule it for +load if * it implements a +load method. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
void add_class_to_loadable_list(Class cls)
{
    IMP method;

    loadMethodLock.assertLocked(a); method = cls->getLoadMethod(a);if(! method)return;  // Don't bother if cls has no +load method
    
    if (PrintLoading) {
        _objc_inform("LOAD: class '%s' scheduled for +load", 
                     cls->nameForLogging());
    }
    
    if (loadable_classes_used == loadable_classes_allocated) {
        loadable_classes_allocated = loadable_classes_allocated*2 + 16;
        loadable_classes = (struct loadable_class *)
            realloc(loadable_classes,
                              loadable_classes_allocated *
                              sizeof(struct loadable_class));
    }
    
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}


/*********************************************************************** * add_category_to_loadable_list * Category cat's parent class exists and the category has been attached * to its class. Schedule this category for +load after its parent class * becomes connected and has its own +load method called. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
void add_category_to_loadable_list(Category cat)
{
    IMP method;

    loadMethodLock.assertLocked(a); method = _category_getLoadMethod(cat);// Don't bother if cat has no +load method
    if(! method)return;

    if (PrintLoading) {
        _objc_inform("LOAD: category '%s(%s)' scheduled for +load", 
                     _category_getClassName(cat), _category_getName(cat));
    }
    
    if (loadable_categories_used == loadable_categories_allocated) {
        loadable_categories_allocated = loadable_categories_allocated*2 + 16;
        loadable_categories = (struct loadable_category *)
            realloc(loadable_categories,
                              loadable_categories_allocated *
                              sizeof(struct loadable_category));
    }

    loadable_categories[loadable_categories_used].cat = cat;
    loadable_categories[loadable_categories_used].method = method;
    loadable_categories_used++;
}



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

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

    void *pool = objc_autoreleasePoolPush(a);do {
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            call_class_loads(a); }// 2. Call category +loads ONCE
        more_categories = call_category_loads(a);// 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}

static void call_class_loads(void)
{
    int i;
    
    // Detach current loadable list.
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used;
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0;
    
    // Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        load_method_t load_method = (load_method_t)classes[i].method;
        if(! cls)continue; 

        if (PrintLoading) {
            _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
        }
        (*load_method)(cls, @selector(load));
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

static bool call_category_loads(void)
{
    int i, shift;
    bool new_categories_added = NO;
    
    // Detach current loadable list.
    struct loadable_category *cats = loadable_categories;
    int used = loadable_categories_used;
    int allocated = loadable_categories_allocated;
    loadable_categories = nil;
    loadable_categories_allocated = 0;
    loadable_categories_used = 0;

    // Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
        Category cat = cats[i].cat;
        load_method_t load_method = (load_method_t)cats[i].method;
        Class cls;
        if(! cat)continue;

        cls = _category_getClass(cat);
        if (cls  &&  cls->isLoadable()) {
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s(%s) load]\n", 
                             cls->nameForLogging(), 
                             _category_getName(cat));
            }
            (*load_method)(cls, @selector(load)); cats[i].cat = nil; }}// Compact detached list (order-preserving)
    shift = 0;
    for (i = 0; i < used; i++) {
        if (cats[i].cat) {
            cats[i-shift] = cats[i];
        } else {
            shift++;
        }
    }
    used -= shift;

    // Copy any new +load candidates from the new list to the detached list.
    new_categories_added = (loadable_categories_used > 0);
    for (i = 0; i < loadable_categories_used; i++) {
        if (used == allocated) {
            allocated = allocated*2 + 16;
            cats = (struct loadable_category *)
                realloc(cats, allocated *
                                  sizeof(struct loadable_category));
        }
        cats[used++] = loadable_categories[i];
    }

    // Destroy the new list.
    if (loadable_categories) free(loadable_categories);

    // Reattach the (now augmented) detached list. 
    // But if there's nothing left to load, destroy the list.
    if (used) {
        loadable_categories = cats;
        loadable_categories_used = used;
        loadable_categories_allocated = allocated;
    } else {
        if (cats) free(cats);
        loadable_categories = nil;
        loadable_categories_used = 0;
        loadable_categories_allocated = 0;
    }

    if (PrintLoading) {
        if(loadable_categories_used ! =0) {
            _objc_inform("LOAD: %d categories still waiting for +load\n", loadable_categories_used); }}return new_categories_added;
}
Copy the code

Invocation path

/*********************************************************************** * _objc_init * Bootstrap initialization. Registers our image notifier with dyld. * Called by libSystem BEFORE library initialization time * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /

void _objc_init(void)

void load_images(const char *path __unused, const struct mach_header *mh)

void prepare_load_methods(const headerType *mhdr)

static void schedule_class_load(Class cls)

void add_class_to_loadable_list(Class cls)

void add_category_to_loadable_list(Category cat)

void call_load_methods(void)

static void call_class_loads(void)

static bool call_category_loads(void)
Copy the code