Load order of classification methods

First let’s define onePersonClass, and to givePersonAdd a- (void)walkMethod and implement the method, and then givePersonAdd aPerson+studentAnd also implemented- (void)walkMethod, and instantiate an objectpAnd letpcall- (void)walkMethod, and printPersonClass.

By printing, we can see that the walk method for classification is executed, and by printing the list of Person methods, we find that there are two walk methods, the first being the walk method for classification.

Here we can explore the classification loading process through the Runtime source code.

  • Step 1: Runtime the first method to execute, where the system executes a series of init initialization methods
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(); //map_images loads the image file _dyLD_OBJC_NOTIFY_register (&map_images, load_images, unmap_image); #if __OBJC2__ didCallDyldNotifyRegister = true; #endif }Copy the code
  • Step 2: Point tomap_images Method. This is where all the image files are loaded, and this is where we look only at the methods associated with category loading.
//1
void
map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])

//2.
void 
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])

//3.
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)

//4.
static void load_categories_nolock(header_info *hi)
Copy the code
  • Step 3: By clicking into the method step by step, we come toload_categories_nolock Methods, this is the process of categorizing methods to add to the list of methods.
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. / / instance methods related to the if (cat - > instanceMethods | | cat - > separate protocols || cat->instanceProperties) { if (cls->isRealized()) { attachCategories(cls, &lc, 1, ATTACH_EXISTING); } else { objc::unattachedCategories.addForClass(lc, cls); }} / / class methods related to the if (cat - > classMethods | | cat - > separate 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
  • Step 4: FindattachCategories Method and enter
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                 int flags)
{
    if (slowpath(PrintReplacedMethods)) {
        printReplacements(cls, cats_list, cats_count);
    }
    if (slowpath(PrintConnecting)) {
        _objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
                     cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
                     cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
    }

    /*
     * Only a few classes have more than 64 categories during launch.
     * This uses a little stack, and avoids malloc.
     *
     * Categories must be added in the proper order, which is back
     * to front. To do that with the chunking, we iterate cats_list
     * from front to back, build up the local buffers backwards,
     * and call attachLists on the chunks. attachLists prepends the
     * lists, so the final result is in the expected order.
     */
    constexpr uint32_t ATTACH_BUFSIZ = 64;
    method_list_t   *mlists[ATTACH_BUFSIZ];
    property_list_t *proplists[ATTACH_BUFSIZ];
    protocol_list_t *protolists[ATTACH_BUFSIZ];

    uint32_t mcount = 0;
    uint32_t propcount = 0;
    uint32_t protocount = 0;
    bool fromBundle = NO;
    bool isMeta = (flags & ATTACH_METACLASS);
    auto rwe = cls->data()->extAllocIfNeeded();

    for (uint32_t i = 0; i < cats_count; i++) {
        auto& entry = cats_list[i];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            if (mcount == ATTACH_BUFSIZ) {
                prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
                rwe->methods.attachLists(mlists, mcount);
                mcount = 0;
            }
            mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        property_list_t *proplist =
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            if (propcount == ATTACH_BUFSIZ) {
                rwe->properties.attachLists(proplists, propcount);
                propcount = 0;
            }
            proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
        }

        protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
        if (protolist) {
            if (protocount == ATTACH_BUFSIZ) {
                rwe->protocols.attachLists(protolists, protocount);
                protocount = 0;
            }
            protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
        }
    }

    if (mcount > 0) {
        prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) flushCaches(cls);
    }

    rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);

    rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
Copy the code

This is where the methods of the class are posted to the method list of the class. Methods in a category are only added before the same methods.

Methods with the same name add order

Like here if you have multiple categoriesPerson+studentwithPerson+TeacherIf there are methods of the same name in the category, based on the order of the category files,Person+ student Methods with the same name in the category are added before the same methods. So if you call a method with the same name, it will executePerson+ student Methods in classification.

loadMethod invocation flow

Void load_images(const char *path __unused, const struct mach_header *mh) {if (! didInitialAttachCategories && didCallDyldNotifyRegister) { didInitialAttachCategories = true; loadAllCategories(); } // 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 the load method (call_load_methods(); Void prepare_load_methods(const headerType * MHDR) {size_t count, I; runtimeLock.assertLocked(); Classref_t const * classList = _getObjc2NonlazyClassList(MHDR, &count); for (i = 0; i < count; i++) { schedule_class_load(remapClass(classlist[i])); } / / traverse load classification method of load 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->superclass); add_class_to_loadable_list(cls); cls->setInfo(RW_LOADED); 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_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
  1. +load method load order: superclass > Subclass > category (load method will be loaded)

Note: if the classification of A, B, in order to see A, B to join engineering in order to compile the first call), first possible results: (superclass > Subclass > category A> category B) (Superclass > Subclass > Category B> category A) (superclass > Subclass > Category B> category A) (superclass > Subclass > Category B> Category A) 3. +load is called before main.

The initialize method

Initialize method call timing: The current self will be called when the first message is received, so consider putting the processing in the load method into this method. Because the load method is executed before the main function, it will affect the app startup speed.

The +initialize method can be loaded in the following four ways: (1) classification (not the initialize method, exist or not initialize the parent class method) (2) classification > subclasses (see compile order multiple classification, only there is a) and (3) the parent class > subclasses (classification not initialize method) (4) the parent class (subclass, The initialize method is not used by the initialize method.

  1. When the + initialize method is called on a subclass, the parent class is called first. If the parent class has a class, the + initialize method in the parent class class overrides the parent class.
  2. The class + initialize overrides the parent class
  3. The + initialize of a subclass does not override the class
  4. The + initialize of the parent class is not necessarily called because it could be overridden by the parent class’s class
  5. Occurs after main.

Continue to update the corrections……