One, foreword

When starting the APP, DYLD will load and link the dynamic library, and then go to libobjC.a. Dylib library to call _objc_init to process the class, and map the whole image file through map_images. Load the image file through read_images. Now that the class has been loaded, what is the process of loading the class? How are class properties, methods, and protocols loaded? Let’s start the whole article with _objc_init. Attached is the objC source download link.

Second, _objc_init source code analysis

void _objc_init(void)
{
  static bool initialized = false;
  if (initialized) return;
  initialized = true; // Some operations on environ_init(); tls_init(); // system-level c++ constructor call static_init(); // Empty function, reserved lock_init(); // Register callback exception_init() to listen for exceptions; // Register the callback notification _dyLD_OBJC_notify_register (&map_images, load_images, unmap_image); }Copy the code

1. Environ_init analysis

The environ_init method reads the environment variables that affect the runtime and ends with an internal code that prints the environment variables. I extracted it, without judgment, and printed it as follows. It is also possible to print environment variables on the terminal using the export OBJC_HELP=1 directive.

Xcode

2. Environ_init analysis

This is mainly the binding of thread keys.

void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
    _objc_pthread_key = TLS_DIRECT_KEY;
    pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
    _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
Copy the code

3. Static_init analysis

Run the C++ static constructor before dyld calls our custom constructor.

static void static_init()
{
    size_t count;
    auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
    for(size_t i = 0; i < count; i++) { inits[i](); }}Copy the code

4. Lock_init analysis

Empty function, guess what reservation might be made for later.

void lock_init(void)
{
}
Copy the code

5. Exception_init analysis

Initialize the libobJC library’s exception handling system, register callbacks to listen for exception crashes, and when a crash occurs, it comes to _objc_Terminate.

void exception_init(void)
{
    old_terminate = std::set_terminate(&_objc_terminate);
}
static void _objc_terminate(void)
{
    if (PrintExceptions) {
        _objc_inform("EXCEPTIONS: terminating");
    }

    if (! __cxa_current_exception_type()) {
        // No current exception.
        (*old_terminate)();
    }
    else {
        // There is a current exception. Check ifIt's an objc exception. @try {__cxa_rethrow(); } @catch (id e) {// It's an objc object. Call Foundation's handler,ifany. (*uncaught_handler)((id)e); (*old_terminate)(); } @catch (...) {// It's not an objc object. Continue to C++ terminate. (*old_terminate)(); }}}Copy the code

_DYLD_OBJC_notify_register

The whole objC in this case is a runtime environment, and when the runtime loads some information about all the classes, it relies on the callback notification of the registration function to tell the current dyLD what it is doing and what environment you need to communicate with each other, such as the current map_images.

Knowledge preparation: The difference between lazy and non-lazy classes

A non-lazy class implements a load method inside a class, so the class loads ahead of time. A lazy class doesn’t implement a load method, so it loads the first time it uses it. When we send a message to that class again, if it’s the first time, During the message lookup process, the class is determined whether it is loaded or not.

1. Map_images analysis

The map_images method is invoked when the image is loaded into memory. Ignore the code for internal functions to jump, print, and manipulate hCount, and you end up at _read_images.

2. _read_images analysis

(1) _read_imagesMethod first creates two tables to store the class.
// If it is the first time to enter, you will leaveifThe following codeif (!doneOnce) {// Why only Once, because the first time you came in, there were no classes, protocols, sel, classes // need to create a container to hold these things, here are two tables.doneOnce = YES; / /... Ignore code that doesn't matterif (DisableTaggedPointers) {
            disableTaggedPointers(); } / / TaggedPointer optimization initializeTaggedPointerObfuscator (); Int namedClassesSize = (isPreoptimized()? unoptimizedTotalClasses : totalClasses) * 4 / 3; // As long as you don't have any classes in the shared cache, Gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize); AllocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil); }Copy the code
(2) readClassAnalysis of the
for(EACH_HEADER) {classref_t * classList = _getObjc2ClassList(hi, &count);if(! MustReadClasses (hi)) {// The image is sufficiently optimized that we don't need to call itreadClass()
            continue;
        }

        bool headerIsBundle = hi->isBundle();
        bool headerIsPreoptimized = hi->isPreoptimized();
        
        for(i = 0; i < count; I++) {OS_dispatch_queue_concurrent, OS_xpc_object, NSRunloop, CF, Fundation, libdispatch, etc. Class CLS = (Class) classList [I]; / / byreadThe Class function gets the new Class after processing. The main internal operations are ro and the RW structure Class newCls =readClass(cls, headerIsBundle, headerIsPreoptimized); // Initialize all lazy-loaded classes and add all future classes to an array // Now data is not loaded, even the class is not initializedif(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. realloc(resolvedFutureClasses, (resolvedFutureClassCount+1) * sizeof(Class)); resolvedFutureClasses[resolvedFutureClassCount++] = newCls; } } } ClassreadClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized) { const char *mangledName = cls->mangledName(); // If a CLS superclass is weak-linked and missing, return YES.if (missingWeakSuperclass(cls)) {
        if (PrintConnecting) {
            _objc_inform("CLASS: IGNORING class '%s' with "
                         "missing weak-linked superclass", cls->nameForLogging()); } // add to remap table, map to nil addRemappedClass(CLS, nil); cls->superclass = nil;returnnil; } / /... Ignore some irrelevant code CLS - > fixupBackwardDeployingStableSwift (); Class replacing = nil; // Special processing for future classes of ro and RW will not be includedif(Class newCls = popFutureNamedClass(mangledName)) {// Copy objC_class into the structure of the future Class // save the future Class rw class_rw_t *rw = newCls->data(); const class_ro_t *old_ro = rw->ro; memcpy(newCls, cls, sizeof(objc_class)); rw->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) {// Assert (getClassExceptSomeSwift(mangledName)); }elseReplacing CLS (CLS, mangledName, Replacing CLS) {// Replace CLS in the GDB_objC_realized_classes table. // Insert CLS into the allocatedClasses table addClassTableEntry(CLS); } // for future reference: shared caches never contain MH_bundlesif(headerIsBundle) { cls->data()->flags |= RO_FROM_BUNDLE; cls->ISA()->data()->flags |= RO_FROM_BUNDLE; } // At this point, the class is present in the entire table, returnreturn cls;
}
Copy the code
(3) remapClassRef
// Fix remapping -! NoClassesRemapped () here forfalse// Unmapped classes and Super classes will be remapped. All remapped classes are non-lazy-loaded classesif(! noClassesRemapped()) {forClass *classrefs = _getObjc2ClassRefs(hi, &count); (EACH_HEADER) {// Remap the Class from the _getObjc2ClassRefs function.for (i = 0; i < count; i++) {
            remapClassRef(&classrefs[i]);
        }
        // fixme why doesn’t test future1 catch the absence of this?
        classrefs = _getObjc2SuperRefs(hi, &count);
        for(i = 0; i < count; i++) { remapClassRef(&classrefs[i]); }}}Copy the code
(4) sel_registerNameNoLock
Static size_t UnfixedSelectors; static size_t UnfixedSelectors; { mutex_locker_t lock(selLock);for (EACH_HEADER) {
        if (hi->isPreoptimized()) continue;
        bool isBundle = hi->isBundle();
        SEL *sels = _getObjc2SelectorRefs(hi, &count);
        UnfixedSelectors += count;
        for(i = 0; i < count; I ++) {// sel_cname converts SEL to char const char *name = sel_cname(sels[I]); Sels [I] = sel_registerNameNoLock(name, isBundle); }}}Copy the code
(5) fixupMessageRef
// Fix the legacy of old function pointer callsfor (EACH_HEADER) {
    message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
    if (count == 0) continue;
    if (PrintVtables) {
         _objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
                     "call sites in %s", count, hi->fname());
    }
    for(i = 0; i < count; I ++) {// Internally register the common Pointers to alloc and objc_msgSend and fix the new Pointers to fixupMessageRef(refs+ I); // Internally register the common Pointers to alloc and objc_msgSend and fix the new Pointers to fixupMessageRef(refs+ I); }}Copy the code
(6) readProtocol
// Iterate over all Protocol lists and load the Protocol list into the Protocol hash tablefor (EACH_HEADER) {
    extern objc_class OBJC_CLASS_$_Protocol; // CLS = Protocol. All protocols and objects have similar structures. Isa corresponds to Protocol Class CLS = (Class)&OBJC_CLASS_$_Protocol; assert(cls); NXMapTable *protocol_map = protocols(); bool isPreoptimized = hi->isPreoptimized(); bool isBundle = hi->isBundle(); Protocol Protocol_t **protolist = _getObjc2ProtocolList(hi, &count);for (i = 0; i < count; i++) {
        readProtocol(protolist[i], cls, protocol_map, isPreoptimized, isBundle); }}Copy the code
(7) remapProtocolRef
// Fix protocol list references, the optimized images may be correct, but not certainfor(EACH_HEADER) {// Note that the following function is _getObjc2ProtocolRefs, Protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);for(i = 0; i < count; i++) { remapProtocolRef(&protolist[i]); }}Copy the code
(8) realizeClassWithoutSwiftAnalysis of the

Initializing the class is in this step, first reading the non-lazy-loaded class from Mach-O and then instantiating the RW with realizeClassWithoutSwift.

// Implement non-lazy-loaded classes, for load methods and static instance variablesfor (EACH_HEADER) {
    classref_t *classlist = 
        _getObjc2NonlazyClassList(hi, &count);
    for (i = 0; i < count; i++) {
        Class cls = remapClass(classlist[i]);
        if(! cls)continue; / /... Ignore some operations on CLS cache addClassTableEntry(CLS); / /... // Implement all non-lazily loaded classes (instantiate some information about the class object, such as RW) realizeClassWithoutSwift(CLS); } // The resolvedFutureClasses array was added in the second stepif (resolvedFutureClasses) {
    for (i = 0; i < resolvedFutureClassCount; i++) {
        Class cls = resolvedFutureClasses[i];
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class is not allowed to be future"); } // Implement lazy class realizeClassWithoutSwift(CLS); cls->setInstancesRequireRawIsa(false/*inherited*/);
    }
    free(resolvedFutureClasses);
}  

static Class realizeClassWithoutSwift(Class cls)
{
    runtimeLock.assertLocked();

    const class_ro_t *ro;
    class_rw_t *rw;
    Class supercls;
    Class metacls;
    bool isMeta;

    if(! cls)returnnil; // Check whether CLS has been initialized. Data ()->flagsif (cls->isRealized()) returncls; assert(cls == remapClass(cls)); Ro = (const class_ro_t *) CLS ->data(); // Determine if the class is an unimplemented future classifRw = CLS ->data(); ro = cls->data()->ro; / / modify flags CLS - > changeInfo (RW_REALIZED | RW_REALIZING, RW_FUTURE); }else{// Normal class. Allocates writable class data. Rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1); rw->ro = ro; rw->flags = RW_REALIZED|RW_REALIZING; cls->setData(rw); } // check whether the metaclass isMeta = ro->flags & RO_META; rw->version = isMeta ? 7:0; // Select an index for this class // Set CLS ->instancesRequireRawIsa if no more indexes are available CLS -> ChooassArrayIndex ();if (PrintConnecting) {
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : ""); } supercls = realizeClassWithoutSwift(CLS ->superclass); metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));#if SUPPORT_NONPOINTER_ISAIsa bool instancesRequireRawIsa = CLS ->instancesRequireRawIsa(); bool rawIsaIsInherited =false;
    static bool hackedDispatch = false; // Disable non-pointer ISAsif(DisableNonpointerIsa) {// Non-pointer ISA disables the environment or application SDK version instancesRequireRawIsa =true;
    }
    else if(! hackedDispatch && ! (ro->flags & RO_META) && 0 == strcmp(ro->name,"OS_object"// Isa also acts as a virtual table pointer to hackedDispatch =true;
        instancesRequireRawIsa = true;
    }
    else if(Supercls && Supercls ->superclass && supercls->instancesRequireRawIsa()) {// Set instancesRequireRawIsa = from the metaclass to the root metaclasstrue;
        rawIsaIsInherited = true;
    }
    
    if (instancesRequireRawIsa) {
        cls->setInstancesRequireRawIsa(rawIsaIsInherited);
    }
// SUPPORT_NONPOINTER_ISA
#endif// Update parent and metaclass CLS ->superclass = supercls; // Update parent and metaclass CLS ->superclass = supercls; cls->initClassIsa(metacls); // Coordinate offset/layout of instance variables, possibly reallocate class_ro_t, update our ro variables.if(supercls && ! isMeta) reconcileInstanceVariables(cls, supercls, ro); // Start setting fastInstanceSize if you haven't already. cls->setInstanceSize(ro->instanceSize); // Copy some flags from ro to RWif (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor(); }} // Propagate the associated object ban flag from ro or its parentif((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) || (supercls && supercls->forbidsAssociatedObjects())) { rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS; } // Connect this class to a list of subclasses of its parent class, i.e., bidirectional bindingif (supercls) {
        addSubclass(supercls, cls);
    } else{ addRootClass(cls); } // Fix the method list, protocol list, and attribute list of CLS, and attach any unfinished class methodizeClass(CLS);return cls;
}
Copy the code

MethodizeClass is a method that attaches ro methods, protocols, and attributes to rW and a class that adds methods, protocols, and attributes to rW. This is when classes are added to this class.

static void methodizeClass(Class cls) { runtimeLock.assertLocked(); bool isMeta = cls->isMetaClass(); auto rw = cls->data(); auto ro = rw->ro; Method_list_t *list = ro->baseMethods();if(list) { prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls)); rw->methods.attachLists(&list, 1); } property_list_t *proplist = ro->baseProperties;if(proplist) { rw->properties.attachLists(&proplist, 1); Protocol_list_t *protolist = ro->baseProtocols;if(protolist) { rw->protocols.attachLists(&protolist, 1); } // The root class gets additional method implementations if they don't already have one. These apply before category substitution.if (cls->isRootMetaclass()) {
        // SEL SEL_initialize = NULL;
        addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO); } // Attach categories. // Return the list of unattached categories for the class and remove them from the list. category_list *cats = unattachedCategoriesForClass(cls,true/*realizing*/); AttachCategories (CLS, cats,false/ * don 't flush caches * /);if(cats) free(cats); / /... Ignore some irrelevant code}Copy the code

The frequency of attachLists is very high. Basically, methods, protocols and attributes are attached to the corresponding list through attachLists function. This method will be introduced separately next.

void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;

        if (hasArray()) {
            // many lists -> many lists
            uint32_t oldCount = array()->count;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount))); array()->count = newCount; // // move the addedLists to array, memmove will check the copied data to ensure that the memory is not overwritten, if it is found that the data will be overwritten, the simple implementation is to switch the starting position of the copy, Copy memmove(array()-> Lists + addedCount, array()-> Lists, oldCount * sizeof(array()->lists[0])); // Copy addedLists to array()-> Lists, memcpy is used if there is overlap between two regions, the result is unpredictable, Memcpy (array()->lists, addedLists, addedCount * sizeof(array()->lists[0])); }else if(! list && addedCount == 1) { // 0 lists -> 1 list list = addedLists[0]; }else{// 1 list -> many lists list * oldList = list; uint32_t oldCount = oldList ? 1:0; uint32_t newCount = oldCount + addedCount;setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if(oldList) array()->lists[addedCount] = oldList; memcpy(array()->lists, addedLists, addedCount * sizeof(array()->lists[0])); }}Copy the code
(9) addUnattachedCategoryForClass
// Discover and process 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);if(! CLS) {// Class target class missing (possibly weak link) catList [I] = nil;if (PrintConnecting) {
                _objc_inform("CLASS: IGNORING category \? \? \? (%s) %p with "
                             "missing weak-linked target class", 
                             cat->name, cat);
            }
            continue; } // 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 the method, protocol, and property of 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 a Meta Class. // Add a Category to a Meta 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); }} // Initializes all classes loaded from disk, discovering that the Category must be executed last // From the Runtime DebugNonFragileIvars field always isfalseSo it doesn't go into this methodif(DebugNonFragileIvars) { realizeAllClasses(); } / /... Ignore some printed code}Copy the code
Small expansion (10)

In read_images, code like _getObjc2ClassRefs often appears. This is to read the corresponding setion segment from the Mach-O file, and we can see the relevant setion segment through MachOView.

3. Load_images analysis

The load_images function loads and calls the load method, so let’s take a look at the underlying handling of load.

Void load_images(const char *path __unused, const struct mach_header *mh) {// If there is no +load method, return with no lock.if(! hasLoadMethods((const headerType *)mh))return; recursive_mutex_locker_t lock(loadMethodLock); { mutex_locker_t lock2(runtimeLock); // Prepare the load method prepare_load_methods((const headerType *)mh); } // Call load call_load_methods(); }Copy the code

Go to prepare_load_methods to see how the system loads 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 ++) {// loop through the load method to loadable_classes schedule_class_load(remapClass(classlist[I])); } / / 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);
        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"); } // Initialize realizeClassWithoutSwift(CLS) if this class is not initialized; assert(cls->ISA()->isRealized()); Add_category_to_loadable_list (cat); 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; Schedule_class_load (CLS ->superclass); // Load the load method to loadable_classes add_class_to_loadable_list(CLS); cls->setInfo(RW_LOADED); } void add_class_to_loadable_list(Class cls) { IMP method; loadMethodLock.assertLocked(); // load method imp method = CLS ->getLoadMethod(); // If there is no load method, return directlyif(! method)return; 
    
    if (PrintLoading) {
        _objc_inform("LOAD: class '%s' scheduled for +load", cls->nameForLogging()); } / / expansionif(loadable_classes_used == loadable_classes_allocated) { loadable_classes_allocated = loadable_classes_allocated*2 + 16; loadable_classes = (struct loadable_class *) realloc(loadable_classes, loadable_classes_allocated * sizeof(struct loadable_class)); } // loadable_classes add load loadable_classes[loadable_classes_used]. CLS = CLS; loadable_classes[loadable_classes_used].method = method; loadable_classes_used++; }Copy the code

The parent class is loaded first, then the parent class is loaded, and then the class is loaded. Let’s see how we call the load method.

void call_load_methods(void) { static bool loading = NO; bool more_categories; loadMethodLock.assertLocked(); // Ensure only one callif (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();
    // do whileLoop through the load methoddo{// 1. Repeatedly call load of non-lazy-loaded classes until there are no morewhile(loadable_classes_used > 0) { call_class_loads(); } // load () {more_categories = call_category_loads(); // 3. If there are classes or more untried classes, run more loads}while(loadable_classes_used > 0 || more_categories); objc_autoreleasePoolPop(pool); loading = NO; } static void call_class_loads(void) { int i; Loadable_classes struct loadable_class *classes = loadable_classes; int used = loadable_classes_used; loadable_classes = nil; loadable_classes_allocated = 0; loadable_classes_used = 0; // Call the load method stored in loadable_classesfor (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()); } // send load message (*load_method)(CLS, SEL_load); } // Free memoryif (classes) free(classes);
}
Copy the code
  • Summary of theload, it can be concluded that,loadIs called in the following order:Parent class -> This class -> Category.

4. Unmap_image analysis

Unmap_image is used to process a given image that will be unmapped by dyLD.

void 
unmap_image(const char *path __unused, const struct mach_header *mh)
{
    recursive_mutex_locker_t lock(loadMethodLock);
    mutex_locker_t lock2(runtimeLock);
    unmap_image_nolock(mh);
}

void 
unmap_image_nolock(const struct mach_header *mh)
{
    if (PrintImages) {
        _objc_inform("IMAGES: processing 1 newly-unmapped image... \n"); } header_info *hi; // Find the run-time header_info structure for the imagefor(hi = FirstHeader; hi ! = NULL; hi = hi->getNext()) {if (hi->mhdr() == (const headerType *)mh) {
            break; }}if(! hi)return;

    if (PrintImages) {
        _objc_inform("IMAGES: unloading image for %s%s%s\n", 
                     hi->fname(),
                     hi->mhdr()->filetype == MH_BUNDLE ? " (bundle)" : "",
                     hi->info()->isReplacement() ? " (replacement)" : ""); } // Only MH_BUNDLE _unload_image(hi) is currently processed; // Remove header_info removeHeader(hi) from the title list; free(hi); }Copy the code

Four,

  • 1. Class loading comes first_objc_initFunction, execute_dyld_objc_notify_registerAnd then through thedyldregisterObjCNotifiersThe callback to_dyld_objc_notify_registerAnd performmap_images,load_images,unmap_image.
  • 2.map_imagesLoad the image file,_read_imagesRead the image file and load the class.
  • 3. First call_read_images“Will initialize two tablesgdb_objc_realized_classesallocatedClassesStore class information.
  • 4. After initializing the table, callreadClassIs to insert the class intoallocatedClassesIn the table.
  • 5. Then pass againremapClassRefPerform the remapping and repair classesfixupMessageRefFixed legacy of old function pointer calls.
  • 6. After the class is processed, add the protocol and method to register in the hash table, convenient to call it later.
  • 7. Through againrealizeClassWithoutSwiftTo initialize the class, which is to the classrwInstantiate, and willroData assignment torwOn.
  • 8.load_imagesIs theloadMethods are handled and called in the orderParent class -> This class -> Category, and there are multiple categoriesloadIs executed in compile order.
  • 9.unmap_imageIs used to process a given image that will be unmapped by dyLD.