review

We explored the _read_images function, looked at the class processing, looked at the actual readClass flow, and found that ro and RW were not loaded.

realizeClassThe introduction of

Because the main research is the class loading principle, so skip the intermediate process, directly look at the class loading

// Realize non-lazy classes (for +load methods and static instances)
    for (EACH_HEADER) {
        classref_t const *classlist = hi->nlclslist(&count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if(! cls)continue;
            
            const char *mangledName = cls->nonlazyMangledName();
            const char *person = "LKTeacher";
            if (strcmp(mangledName, person) == 0) {
                printf("non-lazy classes ********%s**********",mangledName);
            }
            addClassTableEntry(cls);

            if (cls->isSwiftStable()) {
                if (cls->swiftMetadataInitializer()) {
                    _objc_fatal("Swift class %s with a metadata initializer "
                                "is not allowed to be non-lazy",
                                cls->nameForLogging());
                }
                // fixme also disallow relocatable classes
                // We can't disallow all Swift classes because of
                // classes like Swift.__EmptyArrayStorage} realizeClassWithoutSwift(cls, nil); }}Copy the code

From apple’s comments, we can clearly know that only non-lazy loading will execute the following code, so we add +load methods to LKTeacher. In order to accurately locate LKTeacher class, we add the code used before

const char *mangledName = cls->nonlazyMangledName();
const char *person = "LKTeacher";
if (strcmp(mangledName, person) == 0) {
    printf("non-lazy classes ********%s**********",mangledName);
}
Copy the code

Enter a breakpoint, enter debugging, and finally execute to the realizeClassWithoutSwift method

realizeClassWithoutSwiftTo view

/*********************************************************************** * realizeClassWithoutSwift * Performs first-time initialization on class cls, * including allocating its read-write data. * Does not perform any Swift-side initialization. * Returns the real class structure for the class. * Locking: runtimeLock must be write-locked by the caller **********************************************************************/
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    // Omit the code
    auto ro = (const class_ro_t *)cls->data(); // Get the data address in the 'machO file' and strong-shift it according to the 'class_ro_t' format.
    auto isMeta = ro->flags & RO_META;// Check whether it is a metaclass
    if (ro->flags & RO_FUTURE) {
        // This was a future class. rw data is already allocated.rw = cls->data(); ro = cls->data()->ro(); ASSERT(! isMeta); cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE); }else {
        // Normal class. Allocate writeable class data.
        rw = objc::zalloc<class_rw_t>();// Initialize 'rw', copy 'ro' to 'rw', and set 'bits.data' of 'CLS' to 'rw'
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }

    // Omit the intermediate code

    // Attach categories
    methodizeClass(cls, previously);

    return cls;
}
Copy the code

Add a breakpoint to the start of the method, printro You can seebaseMethodListThere are no methods in it.realizeClassWithoutSwiftWill eventually be implementedmethodizeClassMethods.

methodizeClass

static void methodizeClass(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    bool isMeta = cls->isMetaClass();
    auto rw = cls->data();
    auto ro = rw->ro();
    auto rwe = rw->ext();
    
    // Omit the code
    // Code that is written by itself, which is precisely targeted to its own class, and is not metaclass
    const char *mangledName = cls->nonlazyMangledName();
    const char *person = "LKTeacher";
    if (strcmp(mangledName, person) == 0 && !isMeta) {
        printf("non-lazy classes ********%s**********",mangledName);
    }
    / / end
    
    // Install methods and properties that the class implements itself.
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
        if (rwe) rwe->methods.attachLists(&list, 1);
    }

    property_list_t *proplist = ro->baseProperties;
    if (rwe && proplist) {
        rwe->properties.attachLists(&proplist, 1);
    }

    protocol_list_t *protolist = ro->baseProtocols;
    if (rwe && protolist) {
        rwe->protocols.attachLists(&protolist, 1);
    }

    // Root classes get bonus method implementations if they don't have 
    // them already. These apply before category replacements.
    if (cls->isRootMetaclass()) {
        // root metaclass
        addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
    }

    // Attach categories.
    if (previously) {
        if (isMeta) {
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_METACLASS);
        } else {
            // When a class relocates, categories with class methods
            // may be registered on the class itself rather than on
            // the metaclass. Tell attachToClass to look for those.
            objc::unattachedCategories.attachToClass(cls, previously, ATTACH_CLASS_AND_METACLASS); }}objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
}
Copy the code

Look at the whole code, debug the breakpoint, and find that the RWE does not have a value, so the corresponding criterion does not enter, so focus on the prepareMethodLists method.

prepareMethodLists

static void prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
                   bool baseMethods, bool methodsFromBundle, const char *why)
{
    runtimeLock.assertLocked();

    if (addedCount == 0) return;

    // Omit the code
    // Add method lists to array.
    // Reallocate un-fixed method lists.
    // The new methods are PREPENDED to the method list array.

    for (int i = 0; i < addedCount; i++) {
        method_list_t *mlist = addedLists[i];
        ASSERT(mlist);

        // Fixup selectors if necessary
        if(! mlist->isFixedUp()) { fixupMethodList(mlist, methodsFromBundle,true/*sort*/); }}// If the class is initialized, then scan for method implementations
    // tracked by the class's flags. If it's not initialized yet,
    // then objc_class::setInitialized() will take care of it.
    if (cls->isInitialized()) {
        objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount); objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount); objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount); }}Copy the code

FixupMethodList (mlist, methodsFromBundle, true/*sort*/); Here we sort the methods, enter the methods

static void fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort){ runtimeLock.assertLocked(); ASSERT(! mlist->isFixedUp());// fixme lock less in attachMethodLists ?
    // dyld3 may have already uniqued, but not sorted, the list
    if(! mlist->isUniqued()) { mutex_locker_t lock(selLock);// Unique selectors in list.
        for (auto& meth : *mlist) {
            const char *name = sel_cname(meth.name());
            printf("Unsorted ***%s-- %p***\n",name,meth.name()); meth.setName(sel_registerNameNoLock(name, bundleCopy)); }}// Sort by selector address.
    // Don't try to sort small lists, as they're immutable.
    // Don't try to sort big lists of nonstandard size, as stable_sort
    // won't copy the entries properly.
    if(sort && ! mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {method_t::SortBySELAddress sorter;
        std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
    }
    
    printf("*** Dividing line ***\n");
    for (auto& meth : *mlist) {
        const char *name = sel_cname(meth.name());
        printf("***%s-- %p****\n",name,meth.name());
    }
    
    // Mark method list as uniqued and sorted.
    // Can't mark small lists, since they're immutable.
    if (!mlist->isSmallList()) {
        mlist->setFixedUp();
    }
}
Copy the code

According to the Apple comment, you can see that this is sorting by selector address. Because the M1 device is small lists, it cannot be verified. If you need to verify, please debug it by yourself.

Load exploration of non-lazily loaded classes

We found the loading method for the classrealizeClassWithoutSwiftAdd breakpoints here and remove themLKTeacherThe inside of theloadMethod to view the stack informationYou can see that the whole process is as follows, that is, the class is loaded when it starts to be called

  • lookUpImpOrForward
  • realizeAndInitializeIfNeeded_locked
  • initializeAndLeaveLocked
  • initializeAndMaybeRelock
  • realizeClassMaybeSwiftAndUnlock
  • realizeClassMaybeSwiftMaybeRelock
  • realizeClassWithoutSwift

Exploration of the nature of classification

Add LKPerson and the category LKPerson (LK) (written together for compilation purposes) to the main file, and add attributes and methods to the category

found_category_tThere is ainstance_methodsandclass_methodsThis is because the classification has no metaclass, so both instance methods and class methods exist in the classification. The CPP file was not foundgetandsetMethod, so you can see that the classification cannot add attributes.

struct category_t {
    const char *name;
    classref_t cls;
    WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
    WrappedPtr<method_list_t, PtrauthStrip> classMethods;
    struct protocol_list_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);
    
    protocol_list_t *protocolsForMeta(bool isMeta) {
        if (isMeta) return nullptr;
        else returnprotocols; }};Copy the code

rweWhen was the value assigned

Auto rwe = rw->ext(); , click on ext,

    class_rw_ext_t *ext() const {
        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
    }

    class_rw_ext_t *extAllocIfNeeded() {
        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 {
            return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
        }
    }

    class_rw_ext_t *deepCopy(const class_ro_t *ro) {
        return extAlloc(ro, true);
    }
Copy the code

Find the extAllocIfNeeded() method, search globally for extAllocIfNeeded, and find the following method calls

  • attachCategories
  • objc_class::demangledName
  • class_setVersion
  • addMethods_finish
  • class_addProtocol
  • _class_addProperty
  • objc_duplicateClass