1 introduction

We know from the underlying principle of iOS — the association between DYLD and OBJC that when DYLD is associated with objC, the readClass function in _read_images is used to read the information of classes and associate them. This paper mainly discusses the loading of classes.

2 see first_read_imagesfunction

Since we explore the loading of classes, and we have mainly analyzed the function of _read_images in the association between dyld and OBJC, the underlying principle of iOS, we will mainly look at the relevant part of the code of _read_images function, the above code

// Discover classes. Fix up unresolved future classes. Mark bundle classes. bool hasDyldRoots = dyld_shared_cache_some_image_overridden(); for (EACH_HEADER) { if (! mustReadClasses(hi, hasDyldRoots)) { // Image is sufficiently optimized that we need not call readClass() continue; } classref_t const *classlist = _getObjc2ClassList(hi, &count); bool headerIsBundle = hi->isBundle(); bool headerIsPreoptimized = hi->hasPreoptimizedClasses(); for (i = 0; i < count; i++) { Class cls = (Class)classlist[i]; Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized); if (newCls ! = cls && newCls) { // Class was moved but not deleted. Currently this occurs // only when the new class resolved a future class. // Non-lazily realize the class below. resolvedFutureClasses = (Class *) realloc(resolvedFutureClasses, (resolvedFutureClassCount+1) * sizeof(Class)); resolvedFutureClasses[resolvedFutureClassCount++] = newCls; } } } ts.log("IMAGE TIMES: discover classes");Copy the code

2.1 Analysis CallreadClassPart of the code

2.1.1 No callreadClassPrevious CLS analysis

Without callingreadClass, we read the CLS informationFrom the figure above we can see that the CLS is just a string of addresses at this point

2.1.2 callreadClassAnd then CLS analysis

callreadClassAfter that, we read the CLS informationFrom the figure above, we know that CLS has changed from a list of addresses to the LGPerson class. So we know that it must bereadClsssWhat does the delta function doreadClsssFunction.

3 readClsssFunction analysis

3.1 the source code

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized) { const char *mangledName = cls->mangledName(); const char *LGPersonName = "LGPerson"; if (strcmp(mangledName, LGPersonName) == 0) { auto kc_ro = (const class_ro_t *)cls->data(); Printf ("readClass: this is what I want to study %s \n",LGPersonName); } if (missingWeakSuperclass(cls)) { // No superclass (probably weak-linked). // Disavow any knowledge of this subclass. if (PrintConnecting) { _objc_inform("CLASS: IGNORING class '%s' with " "missing weak-linked superclass", cls->nameForLogging()); } addRemappedClass(cls, nil); cls->superclass = nil; return nil; } cls->fixupBackwardDeployingStableSwift(); Class replacing = nil; if (Class newCls = popFutureNamedClass(mangledName)) { // This name was previously allocated as a future class. // Copy objc_class to future class's struct. // Preserve future's rw data block. if (newCls->isAnySwift()) { _objc_fatal("Can't complete future class request for '%s' " "because the real class is too big.", cls->nameForLogging()); } class_rw_t *rw = newCls->data(); const class_ro_t *old_ro = rw->ro(); memcpy(newCls, cls, sizeof(objc_class)); rw->set_ro((class_ro_t *)newCls->data()); newCls->setData(rw); freeIfMutable((char *)old_ro->name); free((void *)old_ro); addRemappedClass(cls, newCls); replacing = cls; cls = newCls; } if (headerIsPreoptimized && ! replacing) { // class list built in shared cache // fixme strict assert doesn't work because of duplicates // ASSERT(cls  == getClass(name)); ASSERT(getClassExceptSomeSwift(mangledName)); } else { addNamedClass(cls, mangledName, replacing); addClassTableEntry(cls); } // for future reference: shared cache never contains MH_BUNDLEs if (headerIsBundle) { cls->data()->flags |= RO_FROM_BUNDLE; cls->ISA()->data()->flags |= RO_FROM_BUNDLE; } return cls; }Copy the code

Source code in order to better distinguish outLGPerson“, deliberately adding judgmentLGPersonType code

3.2 Code Tracing

Through code debugging, finally executedaddNamedClsssfunctionIn the implementationaddNamedClsssFunction before printing CLS is still a string of addresses. When performing theaddNamedClsssWhen the CLS function is printed it is found pointing toLGPersonSo we getaddNamedClsssA pointing operation is performed on the CLS.

3.3 addNamedClsssfunction

Lazy-loaded and non-lazy-loaded classes

4.1 define

  • Lazy loading class: classes that defer loading until the first message is sent are calledLazy loading class;
  • Non-lazy-loaded classesIn:map_imagesFunction performs_read_imagesThe class loaded when the function is calledNon-lazy-loaded classes
  • Commonly understood as when a class is implemented+loadDelta function, delta function, delta functionNon-lazy-loaded classes, is not implementedLazy loading class

4.2 Lazy loading classAnalysis of the

Not implemented for the LGPerson class+loadThe delta function, we know from the previous analysis_read_imagesIn dealing withNon-lazy-loaded classesCode, we hit the breakpoint and print, as shownBecause LGPerson is not implemented+loadFunction, so lazily loading the class, and therefore not entering our breakpoint. However,LGPersonWhen is the class loaded? We studyNon-lazy-loaded classesThe code found, implementedrealizeClassWithoutSwiftDelta function, so let’s seeLazy loading classIs going to go into this function.

2realizeClassWithoutSwift

And from here we’ve verified thatLazy loading classClass loading delays toThe first message is sent

4.3 Non-lazy-loaded classesAnalysis of the

We letLGPersonimplementation+loadFunction, you don’t have to do anything in it, rightLook at this time_read_imagesIn the relevantNon-lazy-loaded classesThe processing ofAt this point we see that we have entered and executed our original breakpointrealizeClassWithoutSwiftfunction

4.4 Key AnalysisrealizeClassWithoutSwiftfunction

Key code analysis

Class loading summary