As mentioned in the previous article, classes that implement the + load method are not lazy-loaded classes, or otherwise they are.

  1. Non-lazy loading classes:+ load methodIs called before main. This time to be able to guarantee+ load methodTo be called, the class must be loaded in advance.
  • Non-lazy class loading process:_dyld_objc_notify_register() -> read_image -> RealizeClassWithoutSwift) -> methodizeClass->attachListsAssign to RW.
  1. Lazy loading: As the name implies, is not normally loaded, only loaded when needed.

So how do lazy log classes load?

Lazy loading of classes

The lazy-loaded class is actually loaded the first time we use it, when the first message is sent to the class.

We also talked about message sending in previous chapters, and one of the most important methods in message sending is lookUpImpOrForward. We mentioned that! CLS ->isRealized() used to initialize lazily loaded classes. In the object-c environment, after a series of function calls, it will magically come to the realizeClassWithoutSwift that we learned in the previous article.

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    ...
    if(! cls->isRealized()) { cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock); // runtimeLock may have been dropped but is now locked again } ... } static Class realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock) {return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}

static Class
realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock)
{
    return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}

static Class
realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
    lock.assertLocked();

    if(! cls->isSwiftStable_ButAllowLegacyForNow()) { // Non-Swift class. Realize it now with the lock still held. // fixme wrongin the future for objc subclasses of swift classes
        realizeClassWithoutSwift(cls);
        if(! leaveLocked) lock.unlock(); }else {
        // Swift class. We need to drop locks and call the Swift
        // runtime to initialize it.
        lock.unlock();
        cls = realizeSwiftClass(cls);
        assert(cls->isRealized());    // callback must have provoked realization
        if (leaveLocked) lock.lock();
    }

    return cls;
}
Copy the code

Let’s test if lazy loading classes can be satisfied! CLS – > isRealized () conditions

1.1 test a

AKPerson does not implement the + load method. It is a lazy load class. The main program calls the initialization method of [AKPerson alloc].

Conclusion 1: The lazy-loaded class is loaded when it is first called, in the lookUpImpOrForward method in the message lookup process.

1.2 test two

The parent class AKPerson implements the + load method, while the subclass AKStudnet does not. The cache is cleared, and the main program subclass calls the initialization method.

Conclusion 2: The parent class implements + load, and the child class does not implement + load. The parent class is a non-lazy-loaded class and the subclass is a lazy-loaded class.

1.3 test three

The parent class AKPerson does not implement the + load method. The subclass AKStudnet implements the + load method. Cleanup cache, initialization method called by the main program subclass first, and initialization method called by the parent class.

Found that the parent class did not enter! CLS ->isRealized(), whose parent is a lazy loaded class. Because recursive calls to realizeClassWithoutSwift complete the inheritance chain and deal with the parent, metaclass of the current class; If there is a parent class, add the current class to the subclass list

if(! cls)returnnil; . supercls = realizeClassWithoutSwift(remapClass(cls->superclass)); metacls = realizeClassWithoutSwift(remapClass(cls->ISA())); . // Update superclass and metaclassin caseof remapping cls->superclass = supercls; cls->initClassIsa(metacls); . // Connect this class to its superclass`s subclass listsif (supercls) {
    addSubclass(supercls, cls);
} else {
    addRootClass(cls);
}
Copy the code

Conclusion 3: If the subclass implements + load, the parent class will also be loaded when the subclass is loaded. The reason for this is that subclasses are processed by their parents and metaclasses when they are loaded.

Ii. Classification structure

2.1 clang

Create a new AKPerson + Test category

clang -rewrite-objc AKPerson+Test.m -o category.cpp

  • Category is stored in the MachO fileThe __ __DATAtheobjc_catlistIn the
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
	&_OBJC_$_CATEGORY_AKPerson_$_Test};Copy the code
  • The structure of the AKPerson category is as follows
static struct _category_t _OBJC_$_CATEGORY_AKPerson_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"AKPerson",
	0, // &OBJC_CLASS_$_AKPerson,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_AKPerson_$_Test,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_AKPerson_$_Test, 0, 0};Copy the code

2.2 Structure of classification

Search for category_t in objc source

struct category_t { const char *name; // The name of the class, not the name of the class classref_t CLS; Struct method_list_t *instanceMethods; Struct method_list_t *classMethods; Struct protocol_t *protocols; Struct property_list_t *instanceProperties; // Fields below this point are not always present on disk. struct property_list_t *_classProperties; Method_list_t *methodsForMeta(bool isMeta) {if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
Copy the code

Why should classification keep instance methods and class methods separate?

In the process of class and metaclass loading, the instance method exists in the class, and the class method exists in the metaclass, and the place where the method belongs has been determined. Classification is later than the loading of classes and metaclasses.

Three. Classification loading

Now that we know that classes are lazy-loaded and non-lazy-loaded, their loading timing is different, so what about the loading of classes?

Before we analyze this, we need to make it clear that a class must be attached to a class. If there are only classes and no classes, then it does not logically make sense and even if it is implemented, the compiler will ignore it.

Classification loading occurs in two places:

  • _read_imagestheDiscover categories.
  • methodizeClass.
// Discover categories. // Discover all categoriesforCategory_t ** CATList = _getObjc2CategoryList(hi, &count); bool hasClassProperties = hi->info()->hasCategoryClassProperties();for(i = 0; i < count; I ++) {// Inner loop over all Category category_t *cat = catList [I]; Class cls = remapClass(cat->cls); // First, register a Category with the class to which it belongs. If the class is already implemented, the list of methods for the class is reconstructed. bool classExists = NO;if(the cat - > instanceMethods | | cat - > separate protocols | | cat - > instanceProperties) {/ / add a Category to the corresponding value of the Class, Value is the Class all the corresponding category array addUnattachedCategoryForClass (cat, CLS, hi); // Add method, protocol, and property for the Category to the Classif (cls->isRealized()) {
                remethodizeClass(cls);
                classExists = YES;
            }
            if (PrintConnecting) {
                _objc_inform("CLASS: found category -%s(%s) %s", 
                             cls->nameForLogging(), cat->name, 
                             classExists ? "on existing class" : ""); } // Add a Category to the Meta Class. // Add a Category to the Classif (cat->classMethods  ||  cat->protocols  
            ||  (hasClassProperties && cat->_classProperties)) 
        {
            addUnattachedCategoryForClass(cat, cls->ISA(), hi);
            if (cls->ISA()->isRealized()) {
                remethodizeClass(cls->ISA());
            }
            if (PrintConnecting) {
                _objc_inform("CLASS: found category +%s(%s)", cls->nameForLogging(), cat->name); }}}}Copy the code
static void methodizeClass(Class cls)
{
    ...
    // Attach categories.
    category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
    attachCategories(cls, cats, false /*don't flush caches*/); . }Copy the code

We added some debugging code to facilitate positioning

3.1 Classification of lazy loading

Lazy loading class & lazy loading classification

  • Send a message to lazily loaded classes,lookupOrForward -> realizeClassWithoutSwiftStart loading memory
  • methodizeClassHandle parent and metaclass relationships
  • unattachedCategoriesForClassReturns NULL
  • Another place to load the classification is not called

Non-lazy loading & lazy loading classification

After two breakpoint debugging, we found that the lazy classification was not added at runtime. Let’s see if the methods in the classification were added.

  • Program starteddyld -> _objc_init -> map_images -> _read_images -> realizeClassWithoutSwift ->methodizeClassLoad the class into memory
  • methodizeClassHandle parent and metaclass relationships
  • unattachedCategoriesForClasReturns NULL
  • Another place to load the classification is not called

Lazy-loaded categories are not added at runtime, so let’s see if methods in the categories are added.

  • Take a look at class_rw_t
(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2148139008
  version = 7
  ro = 0x00000001000011f0
  methods = {
    list_array_tt<method_t, method_list_t> = {
       = {
        list = 0x0000000100001168
        arrayAndFlag = 4294971752
      }
    }
  }
  properties = {
    list_array_tt<property_t, property_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  protocols = {
    list_array_tt<unsigned long, protocol_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x00007fff92d22080
  demangledName = 0x0000000000000000
}
Copy the code
  • Continue looking at baseMethodList in RO
(lldb) p $8.get(1)
(method_t) $15 = {
  name = "load"
  types = 0x0000000100000f8c "v16@0:8"
  imp = 0x0000000100000c10 (objc-debug`+[AKPerson load] at AKPerson.m:12)
}
(lldb) p $8.get(2)
(method_t) $16 = {
  name = "cate_instanceMethod"
  types = 0x0000000100000f8c "v16@0:8"
  imp = 0x0000000100000da0 (objc-debug`+[AKPerson(test) cate_instanceMethod] at AKPerson+test.m:34)
}
Copy the code

Through the LLDB debugging described above, we found that the methods in our classification have been added to ro.

Conclusion: The lazy-loaded classification is determined at compile time for both lazy-loaded and non-lazy-loaded classes.

3.2 Classification of non-lazy loading

Lazy loading classes & non-lazy loading classes

According to the previous theory, lazily loaded classes were loaded the first time a message was sent, The function call stack should be lookupImpOrForward – > realizeClassMaybeSwiftAndLeaveLocked – > realizeClassMaybeSwiftMaybeRelock – > RealizeClassWithoutSwift – > methodizeClass. Let’s test that out.

This time by unattachedCategoriesForClass take to value, and before that CLS ro no classification in the initialize method:

But what about our function call stack, which instead of sending messages goes through the prepare_load_methods method of load_images?

  • Lazy loading classes are not loaded until the message is sent.
  • But the classification is not lazy load class, the classification will go ahead of timeread_images -> addUnattachedCategoryForClass
  • There is no implementation class at this point, it will be belowprepare_load_methods -> realizeClassWithoutSwift -> unattachedCategoriesForClassThe implementation class information is advanced
/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
*
* Locking: write-locks runtimeLock and loadMethodLock
**********************************************************************/
extern bool hasLoadMethods(const headerType *mhdr);
extern void prepare_load_methods(const headerType *mhdr);

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(); } void prepare_load_methods(const headerType *mhdr) { size_t count, i; runtimeLock.assertLocked(); Classref_t * classList = _getObjc2NonlazyClassList(MHDR, &count); classref_t * classList = _getObjc2NonlazyClassList(MHDR, &count);for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i]));
    }

    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 class
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class extensions and categories on Swift "
                        "classes are not allowed to have +load methods"); } realizeClassWithoutSwift(cls); assert(cls->ISA()->isRealized()); add_category_to_loadable_list(cat); }}Copy the code
  • _getObjc2NonlazyCategoryListGet all of the non-lazy classes, and then iterate over those non-lazy classes, loading the classes that those classes depend on.
  • realizeClassWithoutSwiftMethod to load a class

Conclusion: The non-lazily loaded class implementation is ahead of schedule, so lazily loaded classes are not necessarily loaded only on the first message sent, but also depending on whether there is a non-lazily loaded class. If there is a non-lazily loaded class, then goload_imagesThe inside of theprepare_load_methodsrealizeClassWithoutSwift

Non-lazy loading classes & non-lazy loading classes

The non-lazy loading process we are familiar with, loading in _read_images, is also non-lazy loading.

  1. methodizeClassPlace a breakpoint:

unattachedCategoriesForClass

  1. _read_imagesDiscover categoriesPlace a breakpoint

remethodizeClass

/***********************************************************************
* remethodizeClass
* Attach outstanding categories to an existing class.
* Fixes up cls`s method list, protocol list, and property list.
* Updates method caches for cls and its subclasses.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void remethodizeClass(Class cls)
{
    category_list *cats;
    bool isMeta;

    runtimeLock.assertLocked();

    isMeta = cls->isMetaClass();

    // Re-methodizing: check for more categories
    if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
        if (PrintConnecting) {
            _objc_inform("CLASS: attaching categories to class '%s' %s", 
                         cls->nameForLogging(), isMeta ? "(meta)" : "");
        }
        
        attachCategories(cls, cats, true/*flush caches*/); free(cats); }}Copy the code
  1. remethodizeClassThere is aattachCategoriesmethods
// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order, 
// oldest categories first.
static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if(! cats)return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls->isMetaClass();

    // fixme rearrange to remove these intermediate allocations
    method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
    property_list_t **proplists = (property_list_t **)
        malloc(cats->count * sizeof(*proplists));
    protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats->count * sizeof(*protolists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    int i = cats->count;
    bool fromBundle = NO;
    while (i--) {
        auto& entry = cats->list[i];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

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

        protocol_list_t *protolist = entry.cat->protocols;
        if (protolist) {
            protolists[protocount++] = protolist;
        }
    }

    auto rw = cls->data();

    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    rw->methods.attachLists(mlists, mcount);
    free(mlists);
    if (flush_caches  &&  mcount > 0) flushCaches(cls);

    rw->properties.attachLists(proplists, propcount);
    free(proplists);

    rw->protocols.attachLists(protolists, protocount);
    free(protolists);
}
Copy the code

Note the English notes:

  • Attach method lists and properties and protocols from categories to a class. Attach method lists and properties and protocols from categories to a class
  • Assuming the categories in cats are all loaded and sorted by load order, — the world Assumes that the categories are loaded in the load order
  • Oldest categories first

The principle of attachCategories is basically the same as that of attachLists (refer to the loading of classes) :

  • callattachListsAdd classification methods, attributes, and protocols
  • memmoveMove the original data to the end
  • memcpyCopy the new data to the starting location

Note:

  • Actually,attachCategoriesThis method will only be implemented againNon-lazy load classificationNext will be called, and comeattachCategoriesBefore, depending on whether the class is lazily loaded,
  • If it’s lazy loading, then load_images is handled,
  • If the load is not lazy, it is handled in read_images.

4. Classification summary

  1. Class loading
  • Non-lazy loading classes:+ load methodIs called before main. This time to be able to guarantee+ load methodTo be called, the class must be loaded in advance.
  • Lazy loading: As the name implies, is not normally loaded, only loaded when needed
  1. Classification loading:
  • Non-lazy load classification: not implementedloadMethod, determined at compile time, directly handles data() -ro.
  • Lazy load separation: achievedloadMethod, determined at runtime.
  1. This also shows that the loading of categories is different from the loading of classes. Combining the two, we have the following conclusions:
scenario Class loading Classification loading
Lazy load classification + lazy load class First send Compile time
Lazy loading class + non-lazy loading class _read_images Compile time
Non-lazy loading class + lazy loading class Load_images (non-lazy-loaded classification brings forward our lazy-loaded class implementation) MethodizeClass after class loading
Non-lazy loading class + non-lazy loading class _read_images ReMethodizeClass after class loading

5. Contention on the namesake methods of classes and classifications

If a class has multiple categories, what is the order of method calls?

[AKPerson alloc] sayHi]; [AKPerson alloc] sayHi]; [AKPerson alloc] sayHi]; .

  1. Classification is not implemented+ loadmethods

Response Compile Sources for the last classification

  1. Classification is implemented+ loadmethods

Compile Sources

  1. AKPerson+Test1implementation+ loadMethod,AKPerson+Test2Don’t realize+ loadmethods

  1. AKPerson+Test2implementation+ loadMethod,AKPerson+Test1Don’t realize+ loadmethods

+ load

Conclusion 1:

The general method calls the class first and then the main class.

  • Instead of replacing the existing methods of the class, the classified methods are placed in front of the new list of methods, and the methods of the class are placed behind the new list of methods. This is what we normally call “overwriting” the methods of the class with the same name
  • Because the runtime looks up methods in the order of the list of methods, it returns the IMP as soon as it finds a method with the corresponding name.

Conclusion 2:

  • If the classification is not implemented+loadMethod, then the responseCompile SourcesThe last category
  • If both implement +load, respond ·Compile Sources· the last classification
  • If one of them is implemented+loadMethod, the response is not lazy load classification. Lazy classes are loaded into memory at compile time, not at run time

Six load_images.

In the case of lazy loading class + non-lazy loading class, load_image is called when the class is loaded into memory, so we explore this case.

When we press a breakpoint on the load_image implementation, we find that neither the class nor the class prints the +load method: load_image precedes the +load method

Note the English notes:

  • Discover load methods — prepare_load_methods
  • Call +load methods (without runtimeLock – re-entrant) — call_load_methods

6.1 prepare_load_methods Discover and prepare the +load method

void prepare_load_methods(const headerType *mhdr) { size_t count, i; runtimeLock.assertLocked(); Classref_t * classList = _getObjc2NonlazyClassList(MHDR, &count);for(i = 0; i < count; i++) { schedule_class_load(remapClass(classlist[i])); } / / 2. Get the lazy loading classification list category_t * * categorylist = _getObjc2NonlazyCategoryList (MHDR, & count);for (i = 0; i < count; i++) {
        category_t *cat = categorylist[i];
        Class cls = remapClass(cat->cls);
        
        const class_ro_t *ro = (const class_ro_t *)cls->data();
        const char *cname = ro->name;
        const char *oname = "AKPerson";
        
        if (cname && (strcmp(cname, oname) == 0)) {
            printf("_getObjc2NonlazyClassList Class name :%s - %p Class name :%s \n",cname,cls,cat->name);
        }
        
        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); assert(cls->ISA()->isRealized()); add_category_to_loadable_list(cat); }}Copy the code
/***********************************************************************
* prepare_load_methods
* Schedule +load for classes in this image, any un-+load-ed 
* superclasses in other images, and any categories in this image.
**********************************************************************/
// Recursively schedule +load for cls and any un-+load-ed superclasses.
// cls must already be connected.
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

/***********************************************************************
* 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(); method = _category_getLoadMethod(cat); // Don`t botherif 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++;
}
Copy the code

Prepare_load_methods analysis:

  1. _getObjc2NonlazyClassListTo obtainNon-lazy-loaded classesThe list of
  2. schedule_class_loadWalk through the list of classes
  • To recursively call the parent class+ loadMethod to ensure that the parent class+ loadMethods are ordered before subclasses
  • add_class_to_loadable_listClass + loadmethodsloadable_classesinside
  1. _getObjc2NonlazyCategoryListTo obtainNon-lazy load classificationThe list of
  2. Iterate over the category list
  • realizeClassWithoutSwiftTo prevent the class from not being initialized (if it is already initialized)
  • add_category_to_loadable_listClassification of + loadMethods toloadable_categories

6.2 call_load_methods

Now we know that + load is called in load_images, so how does that happen?

/***********************************************************************
* call_load_methods
* Call all pending class and category +load methods.
* Class +load methods are called superclass-first. 
* Category +load methods are not called until after the parent class`s +load.
* 
* This method must be RE-ENTRANT, because a +load could trigger 
* more image mapping. In addition, the superclass-first ordering 
* must be preserved in the face of re-entrant calls. Therefore, 
* only the OUTERMOST call of this function will do anything, and 
* that call will handle all loadable classes, even those generated 
* while it was running.
*
* The sequence below preserves +load ordering in the face of 
* image loading during a +load, and make sure that no 
* +load method is forgotten because it was added during 
* a +load call.
* Sequence:
* 1. Repeatedly call class +loads until there aren`t any more
* 2. Call category +loads ONCE.
* 3. Run more +loads if: * (a) there are more classes to load, OR * (b) there are some potential category +loads that have * still never been attempted. * Category +loads are only run  once to ensure"parent class first" 
* ordering, even if a category +load triggers a new loadable class 
* and a new loadable category attached to that class. 
*
* Locking: loadMethodLock must be held by the caller 
*   All other locks must not be held.
**********************************************************************/
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
static void call_class_loads(void)
{
    ...
    
    // 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, SEL_load); }... }Copy the code
static bool call_category_loads(void)
{
    ...
    
    // 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, SEL_load); cats[i].cat = nil; }}...return new_categories_added;
}
Copy the code
  1. throughobjc_autoreleasePoolPushPush an automatic release pool
  2. The do while loop
  • Cycle callcall_load_methods, send a message to invoke the class+loadMethods.
  • callcall_category_loads, looped to send messages calling classified+loadMethods.
  • (*load_method)(cls, SEL_load); The procedure for calling + load is the procedure for objc_msgSend(CLS, SEL_load).
  1. throughobjc_autoreleasePoolPopPush out an automatic release pool+loadmethods

7. The initialize

7.1 the initialize principle

Initializes the class before it receives its first message.

Called before the class receives the first message. When the class is not in use, the method may never be called.

It was found in lookUpImpOrForward -> initializeAndLeaveLocked -> initializeAndMaybeRelock -> initializeNonMetaClass.

/***********************************************************************
* class_initialize.  Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
**********************************************************************/
void initializeNonMetaClass(Class cls)
{
    ...
    
    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = cls->superclass;
    if(supercls && ! supercls->isInitialized()) { initializeNonMetaClass(supercls); }... callInitialize(cls); . }Copy the code
void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}
Copy the code
  1. If there is a parent class and there is no parent classisInitialized, recursiveinitializeNonMetaClassParent class (conjecture: call order first parent class after child class)
  2. callInitializeIs a normal message sending (presumably: call order classification overrides the main class)

7.2 Initialize Call Sequence Test

  1. Is it superparent or subclass

Both the AKPerson parent and AKTeacher subclasses implement the Initialize method

  • The main program calls firstThe parent class, after the callA subclass

  • The main program calls firstA subclass, after the callThe parent class

  1. Is the classification overwriting the main class

Both AKPerson + Test and AKTeacher + Test classes implement the initialize method. The main program calls the subclass and the parent class respectively to initialize the method.

  1. Is it only called once

The AKPerson parent class implements the initialize method. The AKTeacher subclass does not implement the initialize method. The main program calls the subclass initialization method.

7.3 the initialize summary

  1. initializeUse the normal message sending mechanism. So the classification overrides the main class when more than one classification is implementedinitializeMethod that performs the classification method that is last loaded into memory.
  2. initializeCalled before the first method of the class or its subclass is called (before the message is sent)
  3. If both parent and child classes are implementedinitializeMethod, when calling a subclass,
  • ifInitialize the parent classMethod is called, onlyA subclass of the initializeMethods;
  • ifInitialize the parent classIf no, call firstInitialize the parent classMethod in the callA subclass of the initializeMethods. (The initialize method is no longer called when the parent class is reinitialized.)
  1. Parent class implementation, child class not implementation, when calling a child class, will be called twiceInitialize the parent classmethods

Eight. Summary

This article mainly studied the lazy loading class non-lazy loading class lazy loading classification non-lazy loading classification loading; + load and + initialize calls. And that’s where the percentage is asked in the interview, hopefully that helps.