This chapter content

  1. What does objC do when it initializes
  2. The analysis of the map_images
  3. The analysis of the load_images

This ZhangMu

Learn what objC does when you go from dyld to objC, which is also a pre-main process. We already know two of objc’s most critical function executions from the dyld loading process. But it’s not clear what these two functions are. The main thing to understand in this chapter is the map_images function

Objc_init source analysis

This method is kind of the starting point for OBJC, and it contains all kinds of initializations. And communicate the execution callback of dyLD. Please look at the source code comments, first understand about, and then a method for analysis, only the analysis of the first few on the line, the other are not how important

Void _objc_init(void) {// Ensure that the initialization method is executed only once. Static bool Initialized = false; if (initialized) return; initialized = true; // fixme defer initialization until an objc-using image is found? // Read the environment variables that affect the runtime. For example, we can use this method to print out those key keys environ_init(); // For thread key locking, TLS (thread_local_storage). For example, the per-thread data destructor tls_init(); // run the c++ static constructor. Objc itself calls its constructor static_init() before dyld is called; // Create table runtime_init(); // Exception handling initializes exception_init(); #if __OBJC2__ // Cache condition initialization cache_t::init(); # implementationWithBlock_init(); # implementationwithblock_init (); // Register dyld and objc notifications, &map_images, add an & to indicate synchronization // map_images is a time-consuming operation, And it's important to map the image to make sure that dyLD is synchronized with the function pointer to objc. _dyLD_OBJC_NOTIFY_register (&map_images, load_images, unmap_image); #if __OBJC2__ didCallDyldNotifyRegister = true; #endif }Copy the code

Environ_init analysis

The source code is no longer sent out, and if you are interested, you can view it yourself. In this method we can print out the relevant environment variables. For console output, set as follows: Edit Scheme -> Run -> Arguments -> Environment Variables.

There are two ways to print its impact on environment variables:

  1. Run the source environment and remove the conditional statement from the code.

2. Terminal inputexport OBJC_HELP=1

For example

For example, the keyword OBJC_PRINT_LOAD_METHODS has the effect of printing out all load methods, or OBJC_PRINT_INITIALIZE_METHODS has the effect of printing out the initialize method, and you’ll get a comment just by looking at the above

Tls_init analysis

Pthread_key_init_np sets the destructor for TLS_DIRECT_KEY. For example, pthread_key_init_np sets the destructor for TLS_DIRECT_KEY. We don’t have to worry about this function, maybe we’ll talk about TLS later, but not yet

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

Static_init analysis

The comment apple gave us on this function is: to run the c++ static constructor, libc calls _objc_init() before dyld calls the static constructor, so we have to do it ourselves. Debugging from objc source code also demonstrates an order in which c++ functions are called before load methods. Note that I mean that the C++ constructor is in the objc source code, which would not be possible if we were actually developing it. So in actual development, the order is load first and then c++ (at this time, c++ is initiated by dyld). For example:

As a result,

Runtime_init analysis

This is runtime preparation, which is runtime initialization. We all know that Runtime maintains several tables, and this initialization is the creation of the table. It’s all essentially ExplicitInit. These are just two of the tables; the class table (all the loaded or unloaded classes globally) is not created here

Void runtime_init (void) {/ / create a classification table objc: : unattachedCategories. The init (32); / / create allocated, yuan table objc: : allocatedClasses. The init (); }Copy the code

Map_images Image file loading analysis

Why this function is so important, we know that at compile time (1. LLVM allocates mach-o things, including ro data allocation, which is irrelevant to us, because ro is loaded data and cannot be changed to be stored on disk) we have already identified the class that we will create in our project. Method, etc., memory size and address allocation (actually not quite right, because it is possible to break this pattern when there is a load method, which is described in class loading) are in Mach-o. After startup, dyld (2. Load the Mach-o file, see the previous article) links to the image file, initializing the main program, etc., and invokes the objc initialization. And in this function, and do what, need to see the source code to study. Let’s just look at map_images_nolock, because map_images is just calling it, but map_images_nolock isn’t that important, is it

map_images_nolock

Table initialization for the associated objects, hash table initialization (which stores reference counts and weak references), AutoreleasePoolPage (which is maintained by Runtime), and AutoreleasePoolPage (which is maintained by Runtime). A class table will also be created later. This function is not really important, what is really important is the _read_images function, which you can also use as an intermediate process. The following source code, I can note all remarks, you do not need to know anything about this function, what the meaning

void map_images_nolock(unsigned mhCount, const char * const mhPaths[], Const struct mach_header * const MHDRS []) {static bool firstTime = YES; header_info *hList[mhCount]; // This is the number of image files in all uint32_t hCount; SelrefCount = 0; selrefCount = 0; // Perform first-time initialization if necessary. // This function is called before ordinary library initializers. // fixme defer initialization until an objc-using image is found? If (firstTime) {if (firstTime) {// did some initialization, we remember the shared cache thing, and what it does is get the shared cache and do some things, and do some environment variables and things like that to control it, it doesn't make any sense to us; } if (PrintImages) {_objc_inform("IMAGES: processing %u newly mapped IMAGES... \n", mhCount); } // Find all images with Objective-C metadata. hCount = 0; // Count classes. Size various table based on the total. int totalClasses = 0; int unoptimizedTotalClasses = 0; { uint32_t i = mhCount; While (I --) {// you can understand that mach-o const headerType * MHDR = (const headerType *) MHDRS [I]; auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses); if (! hi) { // no objc data in this entry continue; } if (mhdr->filetype == MH_EXECUTE) { // Size some data structures based on main executable's size #if __OBJC2__ // If dyld3 optimized the main executable, Then there shouldn't // be any selrefs needed in the dynamic map so we can just init // to a 0 sized map To see if there is a selector in Mach-o hi->hasPreoptimizedSelectors() ) { size_t count; _getObjc2SelectorRefs(hi, &count); selrefCount += count; _getObjc2MessageRefs(hi, &count); selrefCount += count; } #else _getObjcSelectorRefs(hi, &selrefCount); #endif #if SUPPORT_GC_COMPAT // Halt if this is a GC app. if (shouldRejectGCApp(hi)) { _objc_fatal_with_reason (OBJC_EXIT_REASON_GC_NOT_SUPPORTED, OS_REASON_FLAG_CONSISTENT_FAILURE, "Objective-C garbage collection " "is no longer supported."); } #endif } hList[hCount++] = hi; if (PrintImages) { _objc_inform("IMAGES: loading image for %s%s%s%s%s\n", hi->fname(), mhdr->filetype == MH_BUNDLE ? " (bundle)" : "", hi->info()->isReplacement() ? " (replacement)" : "", hi->info()->hasCategoryClassProperties() ? " (has class properties)" : "", hi->info()->optimizedByDyld()?" (preoptimized)":""); } } } // Perform one-time runtime initialization that must be deferred until // the executable itself is found. This needs to be done before // further initialization. // (The executable may not be present in this infoList if the // Executable does not contain objective-C code but objective-C is dynamically loaded later. // // Perform a one-time runtime initialization, which must be deferred until the executable file itself is found. This needs to be done before further initialization. (If the executable does not contain Objective-C code, but Objective-C is loaded dynamically later, then the executable may not appear in this infoList. if (firstTime) { sel_init(selrefCount); // Create table function arr_init(); #if SUPPORT_GC_COMPAT // Reject any GC images linked to the main executable. // We already rejected the app itself above. // Images loaded after launch will be rejected by dyld. for (uint32_t i = 0; i < hCount; i++) { auto hi = hList[i]; auto mh = hi->mhdr(); if (mh->filetype ! = MH_EXECUTE && shouldRejectGCImage(mh)) { _objc_fatal_with_reason (OBJC_EXIT_REASON_GC_NOT_SUPPORTED, OS_REASON_FLAG_CONSISTENT_FAILURE, "%s requires Objective-C garbage collection " "which is no longer supported.", hi->fname()); }} #endif #if TARGET_OS_OSX // Disable +initialize fork safety if the app is too old (< 10.13) fork safety if the app has a // __DATA,__objc_fork_ok section. // if(! dyld_program_sdk_at_least(dyld_platform_version_macOS_10_13)) { // DisableInitializeForkSafety = true; // if (PrintInitializing) { // _objc_inform("INITIALIZE: disabling +initialize fork " // "safety enforcement because the app is " // "too old.)"); // } // } for (uint32_t i = 0; i < hCount; i++) { auto hi = hList[i]; auto mh = hi->mhdr(); if (mh->filetype ! = MH_EXECUTE) continue; unsigned long size; if (getsectiondata(hi->mhdr(), "__DATA", "__objc_fork_ok", &size)) { DisableInitializeForkSafety = true; if (PrintInitializing) { _objc_inform("INITIALIZE: disabling +initialize fork " "safety enforcement because the app has " "a __DATA,__objc_fork_ok section"); } } break; // Assume only one MH_EXECUTE image} #endif} If (hCount > 0) {_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses); } firstTime = NO; // Call image load funcs after everything is set up. // Call image load funcs after everything is set up. loadImageFuncs) { for (uint32_t i = 0; i < mhCount; i++) { func(mhdrs[i]); }}}Copy the code

_read_images

This function is the core start of the map_images process. It’s important, it does a lot of things, it reads Mach-o, it starts with the header. You can think of it as reading an image file into memory. It is roughly divided into the following steps:

  1. Come in for the first time to create a class table, the table contains the name of the class, etc. (not the runtime, are global has been loaded or is not loaded classes, I said don’t load loading means is whether is implemented, it just like any other language classes were fixed compile phase, such as memory size, say OC is runtime dynamic, Because it provides an API to change the structure of a class, but you know, the changed structure is rW, not RO. Ro is determined after compilation), which is slightly more important to know about class table creation

  2. Fix sel after Mach-O has been compiled. Is not important

  3. First, add all classes to the class table created by 1 process lock, and bind the address and name of the class, that is, give the class a name. Then we will deal with the messy error class (that is, the class we deleted, but there is still a piece of memory after deletion, we become futureClass). Slightly more important, you need to know that the class is tabulated and given a name

  4. Fixed some remap classes where the remap was not loaded in, not important

  5. Fix objc_msgSend_fixup, not important

  6. See if there’s a deal, fix it if there is. It’s a discovery agreement. It’s not important

  7. Fix remap protocol, not important

  8. The class is processed, but it does not go at startup, that is, the class is not loaded at this time, it is not important

  9. Handles non-lazily loaded classes, which contain the implementation of the class. important

  10. If any of the 3 process error classes are fixed, they are optimized. Is not important

The source code

The source code is too long to look at

void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
    header_info *hi;
    uint32_t hIndex;
    size_t count;
    size_t i;
    Class *resolvedFutureClasses = nil;
    size_t resolvedFutureClassCount = 0;
    static bool doneOnce;
    bool launchTime = NO;
    TimeLogger ts(PrintImageTimes);

    runtimeLock.assertLocked();

#define EACH_HEADER \
    hIndex = 0;         \
    hIndex < hCount && (hi = hList[hIndex]); \
    hIndex++
    // 1.第一次进来
    if (!doneOnce) {
        doneOnce = YES;
        launchTime = YES;

#if SUPPORT_NONPOINTER_ISA
        // Disable non-pointer isa under some conditions.

# if SUPPORT_INDEXED_ISA
        // Disable nonpointer isa if any image contains old Swift code
        for (EACH_HEADER) {
            if (hi->info()->containsSwift()  &&
                hi->info()->swiftUnstableVersion() < objc_image_info::SwiftVersion3)
            {
                DisableNonpointerIsa = true;
                if (PrintRawIsa) {
                    _objc_inform("RAW ISA: disabling non-pointer isa because "
                                 "the app or a framework contains Swift code "
                                 "older than Swift 3.0");
                }
                break;
            }
        }
# endif

# if TARGET_OS_OSX
        // Disable non-pointer isa if the app is too old
        // (linked before OS X 10.11)
//        if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_11)) {
//            DisableNonpointerIsa = true;
//            if (PrintRawIsa) {
//                _objc_inform("RAW ISA: disabling non-pointer isa because "
//                             "the app is too old.");
//            }
//        }

        // Disable non-pointer isa if the app has a __DATA,__objc_rawisa section
        // New apps that load old extensions may need this.
        for (EACH_HEADER) {
            if (hi->mhdr()->filetype != MH_EXECUTE) continue;
            unsigned long size;
            if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) {
                DisableNonpointerIsa = true;
                if (PrintRawIsa) {
                    _objc_inform("RAW ISA: disabling non-pointer isa because "
                                 "the app has a __DATA,__objc_rawisa section");
                }
            }
            break;  // assume only one MH_EXECUTE image
        }
# endif

#endif

        if (DisableTaggedPointers) {
            disableTaggedPointers();
        }
        
        initializeTaggedPointerObfuscator();

        if (PrintConnecting) {
            _objc_inform("CLASS: found %d classes during launch", totalClasses);
        }

        // namedClasses
        // Preoptimized classes don't go in this table.
        // 4/3 is NXMapTable's load factor
        // objc::unattachedCategories.init(32);
        // objc::allocatedClasses.init();
        
        int namedClassesSize = 
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
        //gdb_objc_realized_classes
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);

        ts.log("IMAGE TIMES: first time tasks");
    }

    // Fix up @selector references
    // 2.将mach-o的sel也就是预编译与编译后的sel进行比对修复
    static size_t UnfixedSelectors;
    {
        mutex_locker_t lock(selLock);
        for (EACH_HEADER) {
            if (hi->hasPreoptimizedSelectors()) continue;

            bool isBundle = hi->isBundle();
            SEL *sels = _getObjc2SelectorRefs(hi, &count);
            UnfixedSelectors += count;
            for (i = 0; i < count; i++) {
                const char *name = sel_cname(sels[i]);
                SEL sel = sel_registerNameNoLock(name, isBundle);
                if (sels[i] != sel) {
                    sels[i] = sel;
                }
            }
        }
    }

    ts.log("IMAGE TIMES: fix up selector references");

    // Discover classes. Fix up unresolved future classes. Mark bundle classes.
    bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
    // 3.对一些错误混乱的类处理
    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");

    // Fix up remapped classes
    // Class list and nonlazy class list remain unremapped.
    // Class refs and super refs are remapped for message dispatching.
    // 4.修复重映射一些没有被镜像文件加载进来的类
    if (!noClassesRemapped()) {
        for (EACH_HEADER) {
            Class *classrefs = _getObjc2ClassRefs(hi, &count);
            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]);
            }
        }
    }

    ts.log("IMAGE TIMES: remap classes");

#if SUPPORT_FIXUP
    // Fix up old objc_msgSend_fixup call sites
    // 5.修复一些消息
    for (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++) {
            fixupMessageRef(refs+i);
        }
    }

    ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
#endif


    // Discover protocols. Fix up protocol refs.
    // 6.如果类里面有协议,读取一下
    for (EACH_HEADER) {
        extern objc_class OBJC_CLASS_$_Protocol;
        Class cls = (Class)&OBJC_CLASS_$_Protocol;
        ASSERT(cls);
        NXMapTable *protocol_map = protocols();
        bool isPreoptimized = hi->hasPreoptimizedProtocols();

        // Skip reading protocols if this is an image from the shared cache
        // and we support roots
        // Note, after launch we do need to walk the protocol as the protocol
        // in the shared cache is marked with isCanonical() and that may not
        // be true if some non-shared cache binary was chosen as the canonical
        // definition
        if (launchTime && isPreoptimized) {
            if (PrintProtocols) {
                _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
                             hi->fname());
            }
            continue;
        }

        bool isBundle = hi->isBundle();

        protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
        for (i = 0; i < count; i++) {
            readProtocol(protolist[i], cls, protocol_map, 
                         isPreoptimized, isBundle);
        }
    }

    ts.log("IMAGE TIMES: discover protocols");

    // Fix up @protocol references
    // Preoptimized images may have the right 
    // answer already but we don't know for sure.
    // 7.修复一些没有被加载的协议
    for (EACH_HEADER) {
        // At launch time, we know preoptimized image refs are pointing at the
        // shared cache definition of a protocol.  We can skip the check on
        // launch, but have to visit @protocol refs for shared cache images
        // loaded later.
        if (launchTime && hi->isPreoptimized())
            continue;
        protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapProtocolRef(&protolist[i]);
        }
    }

    ts.log("IMAGE TIMES: fix up @protocol references");

    // Discover categories. Only do this after the initial category
    // attachment has been done. For categories present at startup,
    // discovery is deferred until the first load_images call after
    // the call to _dyld_objc_notify_register completes. rdar://problem/53119145
    // 8.对于分类的处理,这里条件为默认false
    if (didInitialAttachCategories) {
        for (EACH_HEADER) {
            load_categories_nolock(hi);
        }
    }

    ts.log("IMAGE TIMES: discover categories");

    // Category discovery MUST BE Late to avoid potential races
    // when other threads call the new category code before
    // this thread finishes its fixups.

    // +load handled by prepare_load_methods()

    // Realize non-lazy classes (for +load methods and static instances)
    // 9.对于哪些非懒加载类的加载处理(懒加载类与非懒加载类,加载的时机其实不同)
    for (EACH_HEADER) {
        classref_t const *classlist = hi->nlclslist(&count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;

            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);
        }
    }

    ts.log("IMAGE TIMES: realize non-lazy classes");

    // Realize newly-resolved future classes, in case CF manipulates them
    // 10.上面3的时候如果有类是混乱后被修复,优化一下被侵犯的类
    if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            Class cls = resolvedFutureClasses[i];
            if (cls->isSwiftStable()) {
                _objc_fatal("Swift class is not allowed to be future");
            }
            realizeClassWithoutSwift(cls, nil);
            cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }

    ts.log("IMAGE TIMES: realize future classes");

    if (DebugNonFragileIvars) {
        realizeAllClasses();
    }


    // Print preoptimization statistics
    // 如果说我们设置环境变量OBJC_PRINT_PREOPTIMIZATION这个值会进行打印
    if (PrintPreopt) {
        static unsigned int PreoptTotalMethodLists;
        static unsigned int PreoptOptimizedMethodLists;
        static unsigned int PreoptTotalClasses;
        static unsigned int PreoptOptimizedClasses;

        for (EACH_HEADER) {
            if (hi->hasPreoptimizedSelectors()) {
                _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors "
                             "in %s", hi->fname());
            }
            else if (hi->info()->optimizedByDyld()) {
                _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors "
                             "in %s", hi->fname());
            }

            classref_t const *classlist = _getObjc2ClassList(hi, &count);
            for (i = 0; i < count; i++) {
                Class cls = remapClass(classlist[i]);
                if (!cls) continue;

                PreoptTotalClasses++;
                if (hi->hasPreoptimizedClasses()) {
                    PreoptOptimizedClasses++;
                }
                
                const method_list_t *mlist;
                if ((mlist = cls->bits.safe_ro()->baseMethods())) {
                    PreoptTotalMethodLists++;
                    if (mlist->isFixedUp()) {
                        PreoptOptimizedMethodLists++;
                    }
                }
                if ((mlist = cls->ISA()->bits.safe_ro()->baseMethods())) {
                    PreoptTotalMethodLists++;
                    if (mlist->isFixedUp()) {
                        PreoptOptimizedMethodLists++;
                    }
                }
            }
        }

        _objc_inform("PREOPTIMIZATION: %zu selector references not "
                     "pre-optimized", UnfixedSelectors);
        _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) method lists pre-sorted",
                     PreoptOptimizedMethodLists, PreoptTotalMethodLists, 
                     PreoptTotalMethodLists
                     ? 100.0*PreoptOptimizedMethodLists/PreoptTotalMethodLists 
                     : 0.0);
        _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) classes pre-registered",
                     PreoptOptimizedClasses, PreoptTotalClasses, 
                     PreoptTotalClasses 
                     ? 100.0*PreoptOptimizedClasses/PreoptTotalClasses
                     : 0.0);
        _objc_inform("PREOPTIMIZATION: %zu protocol references not "
                     "pre-optimized", UnfixedProtocolReferences);
    }

#undef EACH_HEADER
}
Copy the code

Load_images, the load call to the load method

This method contains the load call to the load method for all files. The main thing is to call the load method. (As an aside, when a class and a classification implement the load method together, you break the class loading, and the classification loading will take place from here. However, when more than one class implements the load method, that is, two or more classes will still go from here, but the way to go is different.

Void load_images(const char *path __unused, const struct mach_header *mh) {void load_images(const char *path __unused, const struct mach_header *mh) { // if (! didInitialAttachCategories && didCallDyldNotifyRegister) { didInitialAttachCategories = true; loadAllCategories(); } // 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); // Discover load methods {mutex_locker_t lock2(runtimeLock); prepare_load_methods((const headerType *)mh); }}}}}}}}}}} Call_load_methods (); // Call +load methods(without runtimeLock -re-entrant) call_load_methods(); }Copy the code

LoadAllCategories analysis

This method is not important, it’s only done once, and I analyzed it, and it’s only done when both the class and the class implement the load method, and that’s related to the class loading, so I’m not going to analyze it here. The loop is executed 296 times, which may not be accurate, but it doesn’t matter, it’s just fun

static void loadAllCategories() { mutex_locker_t lock(runtimeLock); // For (auto *hi = FirstHeader; hi ! = NULL; Hi = hi->getNext()) {// Categories_nolock (hi); load_categories_nolock(hi); load_categories_nolock(hi); }}Copy the code

Prepare_load_methods Preparation of the load method

This method is a preparation for the load method, and it’s also related to a certain situation in which we’re doing category loading, and I’ll fill in the category loading section later. Take a look at the source code inside the remarks can understand the understanding can not, it does not matter.

void prepare_load_methods(const headerType *mhdr) { size_t count, i; runtimeLock.assertLocked(); Classref_t const * classList = _getObjc2NonlazyClassList(MHDR, &count); Add_class_to_loadable_list for (I = 0; i < count; i++) { schedule_class_load(remapClass(classlist[i])); } / / to obtain a list of the lazy loading classification category_t * const * categorylist = _getObjc2NonlazyCategoryList (MHDR, & count); for (i = 0; i < count; i++) { category_t *cat = categorylist[i]; Class cls = remapClass(cat->cls); if (! cls) continue; Const char *clsName = CLS -> nonlazyMangledName(); const char *clsName = CLS -> nonlazyMangledName(); const char *perosn = "Person"; const char *teacher = "Teacher"; if (strcmp(clsName, perosn) == 0 || strcmp(clsName, teacher) == 0) { printf("-------%s----%s\n", __func__,clsName); } */ if (cls->isSwiftStable()) { _objc_fatal("Swift class extensions and categories on Swift " "classes are not allowed to have +load methods"); } // realizeClassWithoutSwift(CLS, nil); // realizeClassWithoutSwift(CLS, nil); ASSERT(cls->ISA()->isRealized()); // Compare add_category_to_loadable_list(cat) with add_class_to_loadable_list; }}Copy the code