preface

Following the read_images analysis of class loading in the previous part, the process of map_images → _read_images → readClass → addNamedClass → addClassTableEntry was explored in the previous part.

When loading realize non-lazy classes, first insert the class into the table addClassTableEntry, and then call realizeClassWithoutSwift to initialize the operation of the class.

realizeClassWithoutSwift

The main process includes: RW and RO assignment (placeholder) → parent and metaclass loading and association → setting member variables → setting marker bit → methodizeClass assignment (RW and ro real assignment).

Rw and ro assignments

// Get ro from the class
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
// Determine whether the RW has been allocated (future class)
if (ro->flags & RO_FUTURE) {
    // Get ro from future classrw = cls->data(); ro = cls->data()->ro(); ASSERT(! isMeta); cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE); }else {
    // If it is a normal class
    rw = objc::zalloc<class_rw_t>(); // Open up memory
    rw->set_ro(ro); / / set the ro
    rw->flags = RW_REALIZED|RW_REALIZING|isMeta; // Set the tag
    cls->setData(rw); // Set the rW of the class
}
Copy the code

Description:

  • throughcls->data()To obtainroThe data.
  • rwOpen up space, willroAssigned torwthero()In the. (Apple has optimized this accordingly. Please refer toWWDC- About runtime optimization)
  • Set up therwtheflags, includingRW_REALIZED(31) saidrwWhether the initialization is complete;RW_REALIZING(19) saidrwWhether initialization is in progress;isMeta(0 bit) is a metaclass.
  • The assignmentrwtocls.

However, after actual debugging, the baseMethodList is empty after executing the process.

This is not a real assignment, but according to the class offset situation to occupy the corresponding position.

Parent and metaclass loading and association

The parent classandThe metaclassloading

// Load the parent and metaclass recursively
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
Copy the code

Instantiate parent and metaclass recursively

ISAPure pointer processing

#if SUPPORT_NONPOINTER_ISA
    if (isMeta) {
        // the ISA of a metaclass ISA pure pointer
        // Set the raw ISA to the cache
        cls->setInstancesRequireRawIsa();
    } else {
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;
        // OBJC_DISABLE_NONPOINTER_ISA is configured in the corresponding environment variable
        if (DisableNonpointerIsa) {
            Isa isa pure pointer if the environment variable is true.
            instancesRequireRawIsa = true;
        }
        // Pure pointer for os_Object
        else if(! hackedDispatch &&0 == strcmp(ro->getName(), "OS_object"))
        {
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        // If the parent class is a pure pointer and the parent class exists.
        else if (supercls  &&  supercls->getSuperclass()  &&
                 supercls->instancesRequireRawIsa())
        {
            instancesRequireRawIsa = true;
            // rawIsaIsInherited indicates that inherited is a pure pointer
            rawIsaIsInherited = true;
        }
        // If the pure pointer marks bit true
        if (instancesRequireRawIsa) {
            // Recursively sets the ISA of classes and subclasses to a pure pointer, where rawIsaIsInherited is the print flagcls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited); }}// SUPPORT_NONPOINTER_ISA
#endif
Copy the code
  • judgeisaWhether it is a pure pointer.
  • judgeos_object,The parent classEtc.Pure pointerIs it necessary to recursively set pure Pointers?
  • A recursive setclassandA subclasstheisaIs a pure pointer.

The parent classandThe metaclassassociated

// Associate the parent class with the metaclass.
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
Copy the code

Associate a parent class with a metaclass. Corresponding class inheritance chain with ISA go bitmap.

Sets member variables and flag bits

// Fix ivar offset, possibly recreate class_ro_t to update ivar
if(supercls && ! isMeta) reconcileInstanceVariables(cls, supercls, ro);// Set instance size (size of member variable)
cls->setInstanceSize(ro->instanceSize);

// Synchronize the flag bit
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
    cls->setHasCxxDtor();
    if(! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) { cls->setHasCxxCtor(); }}Copy the code
  • In the case of superclasses, and non-metaclasses are fixedivartheoffset
  • To resetMember variablesThe size of the.

methodizeClass

In the above rW and RO assignments, there is no method data (baseMethodList is empty) through the actual test, because the RW at this time is only an empty structure address, where SEL and IMP have not been associated.

static void methodizeClass(Class cls, Class previously)
{
    bool isMeta = cls->isMetaClass();
    auto rw = cls->data();
    auto ro = rw->ro();
    auto rwe = rw->ext();

    // Load its own methods and properties into rWE.
    method_list_t *list = ro->baseMethods();
    if (list) {
        // Prepare and fix the (sort) method list
        prepareMethodLists(cls, &list, 1.YES, isBundleClass(cls), nullptr);
        if (rwe) rwe->methods.attachLists(&list, 1);
    }
    // Load attributes to rwe
    property_list_t *proplist = ro->baseProperties;
    if (rwe && proplist) {
        rwe->properties.attachLists(&proplist, 1);
    }
    // Load the protocol to rWE
    protocol_list_t *protolist = ro->baseProtocols;
    if (rwe && protolist) {
        rwe->protocols.attachLists(&protolist, 1);
    }

    // If it is the root metaclass
    if (cls->isRootMetaclass()) {
        // Add the initialize method
        addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "".NO);
    }

    // Add the methods of the current class to unattachedCategories
    objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);

}
Copy the code
  • addclsMethods and properties torweIn the.
  • Root metaclass judgment, addinitializeMethods.
  • Add the loaded class tounattachedCategoriesIn the table.

Rw and RO data validation

1. When the authentication is performed, it is found thatlistIs empty.

The reason is that the ro at this time is actually the ro of the metaclass, and there is no method in the metaclass, so the metaclass needs to be filtered.

2. Verify again

Terminal View method

Note:

  • If it isFor the first time,loadingclsThere is no waycorrectionAnd here,Don'tPrint out the method. This is because I’ve been debugging beforeThere areCache, so you can print out methods.
  • If there is no fix method, it will be executedprepareMethodListsMake method modification.

prepareMethodLists

static void prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
                   bool baseMethods, bool methodsFromBundle, const char *why)
{
    // Core code
    for (int i = 0; i < addedCount; i++) {
        method_list_t *mlist = addedLists[i];
        if(! mlist->isFixedUp()) {// Fix method (sort)
            fixupMethodList(mlist, methodsFromBundle, true/*sort*/); }}}Copy the code
  • Among themaddedCountThe value of1.addedLists[0]forrothelist.
  • If no correction is made, executefixupMethodListSort.

fixupMethodList

static void fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
    // DyLD3 may have been marked unique, but not sorted
    if(! mlist->isUniqued()) {// Iterate over the list of methods
        for (auto& meth : *mlist) {
            const char *name = sel_cname(meth.name());
            // Get sel name and set.meth.setName(sel_registerNameNoLock(name, bundleCopy)); }}// select * from sel;
    if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
        method_t::SortBySELAddress sorter;
        std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
    }
}
Copy the code

SEL is modified according to name, corresponding to binary search of slow message search.

Sorting validation

Before and after stable_sort above, add the following code to print the method before and after the sort.

for (auto& meth : *mlist) {
    const char *name = sel_cname(meth.name());
    // Prints the method name
    printf("Sorted: %s -- %p\n", name, meth.name());
}
Copy the code

The results are as follows:

Before Sorting: subInstanceMethod1 -- 0x100003A16 Before Sorting: subInstanceMethod2 -- 0x100003A29 Before Sorting: subInstanceMethod3 -- 0x100003A3c Before Sorting: SubInstanceMethod4 -- 0x100003A4F Before sorting: subInstanceMethod5 -- 0x100003A62 Before sorting: init -- 0x100003906 -------------- After sorting: SubInstanceMethod1 -- 0x100003A16 Sorted after: subInstanceMethod2 -- 0x100003A29 Sorted after: subInstanceMethod3 -- 0x100003a3c Sorted after: subInstanceMethod1 -- 0x100003A16 Sorted after: subInstanceMethod2 -- 0x100003A29 Sorted after: SubInstanceMethod4 -- 0x100003A4F After sorting: subInstanceMethod5 -- 0x100003a62 After sorting: init -- 0x7FFF7B905a35Copy the code
  • The order before and after is the same, because it may have been corrected before, or it may have been added in the correct order.
  • initThe correct address was assigned.

Classification inquiry

Classification structure

Create the following categories and view the category structure.

@interface ZLSubObject (ZLExtend)

@property (nonatomic.strong.nullable) NSString *cate_name;

- (void)cate_instanceMethod;
+ (void)cate_classMethod;

@end

@implementation ZLSubObject (ZLExtend)

- (void)cate_instanceMethod {
    NSLog(@"%s",__func__);
}

+ (void)cate_classMethod {
    NSLog(@"%s",__func__);
}

@end
Copy the code

clangTo view

struct _category_t {
    const char *name;
    struct _class_t *cls;
    const struct _method_list_t *instance_methods;
    const struct _method_list_t *class_methods;
    const struct _protocol_list_t *protocols;
    const struct _prop_list_t *properties;
};
Copy the code

The source codeTo view

struct category_t {
    const char *name; / / class name
    classref_t cls; / / class they belong to
    WrappedPtr<method_list_t, PtrauthStrip> instanceMethods; // Attribute method list
    WrappedPtr<method_list_t, PtrauthStrip> classMethods; // List of class methods
    struct protocol_list_t *protocols; / / agreement
    struct property_list_t *instanceProperties; // Attribute list
    struct property_list_t* _classProperties; // Class attribute list (uncommon)
    
    // List of metaclass methods
    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }
    // List of metaclass attributes
    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
    // List of metaclass protocols
    protocol_list_t *protocolsForMeta(bool isMeta) {
        if (isMeta) return nullptr;
        else returnprotocols; }};Copy the code

Rwe assignment process

When methodizeClass is loaded above, we get the value of rwe, but by printing, rwe is null. This is because RWE has a value if there is a classification (from WWDC). The assignment of rwe comes from the rw->ext() function. As follows:

class_rw_ext_t *ext(a) const {
    // get_ro_or_rwe() gets the contents of rwe
    return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
}
Copy the code

Using the backward method, get_ro_or_rwe() is searched globally, and only extAllocIfNeeded() retrives rwe itself.

class_rw_ext_t *extAllocIfNeeded(a) {
    / / for rwe
    auto v = get_ro_or_rwe();
    if (fastpath(v.is<class_rw_ext_t* > ())) {return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
    } else {
        / / create rwe
        return extAlloc(v.get<const class_ro_t*>(&ro_or_rw_ext)); }}Copy the code

This method determines whether an RWE exists, gets and returns it if it does, and creates an RWE if it doesn’t

Global search extAllocIfNeeded, only the attachCategories call fits best.

The invocation logic of attachCategories can be applied in attachToClass and load_categories_NOLock.

conclusion

So far, there are three paths for loading the classification:

  • realizeClassWithoutSwiftmethodizeClassattachToClassattachCategories
  • load_imagesloadAllCategoriesload_categories_nolockattachCategories
  • _read_imagesload_categories_nolockattachCategories

Note: In the third clause, the process prerequisite is executed only after load_images is executed. Only for categories that occur at startup, this is deferred until after the first load_images method of the _DYLD_OBJC_Notify_register is called.

* The specific process of classification loading will be explored later.