美文网首页
OC类的加载

OC类的加载

作者: 会跑的鱼_09 | 来源:发表于2020-12-06 19:00 被阅读0次

    上篇我们在dyld分析分析中了解到在dyld启动过程是会调用objc的init方法,而该init方法中会往dyld中注册一个回调,在dyld后续流程中会调用该回调map_images。

    类的读取

    map_images中主要是调用map_images_nolock,而map_images_nolock主要流程是进行了一些初始化,以及最关键的把所有的在读取到内存中来(注意读取的类并未实现,其ro、rw数据还没有处理好)

    void
    map_images(unsigned count, const char * const paths[],
              const struct mach_header * const mhdrs[])
    {
       mutex_locker_t lock(runtimeLock);
       return map_images_nolock(count, paths, mhdrs);
    }
    void 
    map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                     const struct mach_header * const mhdrs[])
    {
       static bool firstTime = YES;
       header_info *hList[mhCount];
       uint32_t hCount;
       size_t 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) {
           preopt_init();
       }
    
       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--) {
               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__
                   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.
       if (firstTime) {
           //进行全局sel集合的初始化
           sel_init(selrefCount);
           //进行全局的自动释放池、关联对象表、弱引用表、引用计数表的初始化
           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).
           // Disable +initialize fork safety if the app has a
           //   __DATA,__objc_fork_ok section.
    
           if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_13) {
               DisableInitializeForkSafety = true;
               if (PrintInitializing) {
                   _objc_inform("INITIALIZE: disabling +initialize fork "
                                "safety enforcement because the app is "
                                "too old (SDK version " SDK_FORMAT ")",
                                FORMAT_SDK(dyld_get_program_sdk_version()));
               }
           }
    
           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.
       for (auto func : loadImageFuncs) {
           for (uint32_t i = 0; i < mhCount; i++) {
               func(mhdrs[i]);
           }
       }
    }
    

    看一下最关键的_read_images,主要逻辑是:条件控制只执行一次、处理编译阶段SEL混乱的问题、错误的类处理、加载协议、分类处理、类的读取、以及针对某些非懒加载的在直接进行实现。

    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++
    
       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_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11) {
               DisableNonpointerIsa = true;
               if (PrintRawIsa) {
                   _objc_inform("RAW ISA: disabling non-pointer isa because "
                                "the app is too old (SDK version " SDK_FORMAT ")",
                                FORMAT_SDK(dyld_get_program_sdk_version()));
               }
           }
    
           // 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
           int namedClassesSize = 
               (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
           gdb_objc_realized_classes =
               NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
    
           ts.log("IMAGE TIMES: first time tasks");
       }
    
       // Fix up @selector references
       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();
    
       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.
       
       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
       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
    
       bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
    
       // Discover protocols. Fix up protocol refs.
       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 && cacheSupportsProtocolRoots) {
               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.
       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 && cacheSupportsProtocolRoots && 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
       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)
       for (EACH_HEADER) {
           classref_t const *classlist = 
               _getObjc2NonlazyClassList(hi, &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
       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
       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 = ((class_ro_t *)cls->data())->baseMethods())) {
                       PreoptTotalMethodLists++;
                       if (mlist->isFixedUp()) {
                           PreoptOptimizedMethodLists++;
                       }
                   }
                   if ((mlist=((class_ro_t *)cls->ISA()->data())->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
    }
    

    再看一下最关键的readClass,主要就是把类读取到全局的classTable中来

    Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
    {    
       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;
    }
    

    梳理一下类的读取流程:map_images->map_images_nolock->_read_images->readClass->addClassTableEntry,就是读取images中所有的类并把它们加载到全局的classTable中来。

    类的实现

    另外在read_images过程中对于非懒加载的类(实现过load方法的类)会直接对其实现,进入realizeClassWithoutSwift方法中来。而该方法主要是处理类以及其父类元类的关系,加载方法列表(包括分类的方法)。

    static Class realizeClassWithoutSwift(Class cls, Class previously)
    {
       runtimeLock.assertLocked();
    
       class_rw_t *rw;
       Class supercls;
       Class metacls;
       
       if (!cls) return nil;
       if (cls->isRealized()) return cls;
       ASSERT(cls == remapClass(cls));
    
       // fixme verify class is not in an un-dlopened part of the shared cache?
    
       auto ro = (const class_ro_t *)cls->data();
       auto isMeta = ro->flags & RO_META;
       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空间大小的内存
           rw = objc::zalloc<class_rw_t>();
           //填充其数据为ro
           rw->set_ro(ro);
           rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
           //设置类的rw信息!!!
           cls->setData(rw);
       }
    
    #if FAST_CACHE_META
       if (isMeta) cls->cache.setBit(FAST_CACHE_META);
    #endif
    
       // Choose an index for this class.
       // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
       cls->chooseClassArrayIndex();
    
       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)" : "");
       }
    
       //获取其父类和元类信息,建立oc类、父类、元类三者之间的关系
       supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
       metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
    
    #if SUPPORT_NONPOINTER_ISA
       if (isMeta) {
           // Metaclasses do not need any features from non pointer ISA
           // This allows for a faspath for classes in objc_retain/objc_release.
           cls->setInstancesRequireRawIsa();
       } else {
           // Disable non-pointer isa for some classes and/or platforms.
           // Set instancesRequireRawIsa.
           bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
           bool rawIsaIsInherited = false;
           static bool hackedDispatch = false;
    
           if (DisableNonpointerIsa) {
               // Non-pointer isa disabled by environment or app SDK version
               instancesRequireRawIsa = true;
           }
           else if (!hackedDispatch  &&  0 == strcmp(ro->name, "OS_object"))
           {
               // hack for libdispatch et al - isa also acts as vtable pointer
               hackedDispatch = true;
               instancesRequireRawIsa = true;
           }
           else if (supercls  &&  supercls->superclass  &&
                    supercls->instancesRequireRawIsa())
           {
               // This is also propagated by addSubclass()
               // but nonpointer isa setup needs it earlier.
               // Special case: instancesRequireRawIsa does not propagate
               // from root class to root metaclass
               instancesRequireRawIsa = true;
               rawIsaIsInherited = true;
           }
    
           if (instancesRequireRawIsa) {
               cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
           }
       }
    // SUPPORT_NONPOINTER_ISA
    #endif
    
       // Update superclass and metaclass in case of remapping
       cls->superclass = supercls;
       cls->initClassIsa(metacls);
    
       // Reconcile instance variable offsets / layout.
       // This may reallocate class_ro_t, updating our ro variable.
       if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);
    
       // Set fastInstanceSize if it wasn't set already.
       cls->setInstanceSize(ro->instanceSize);
    
       // Copy some flags from ro to rw
       if (ro->flags & RO_HAS_CXX_STRUCTORS) {
           cls->setHasCxxDtor();
           if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
               cls->setHasCxxCtor();
           }
       }
       
       // Propagate the associated objects forbidden flag from ro or from
       // the superclass.
       if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
           (supercls && supercls->forbidsAssociatedObjects()))
       {
           rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
       }
    
       // Connect this class to its superclass's subclass lists
       if (supercls) {
           addSubclass(supercls, cls);
       } else {
           addRootClass(cls);
       }
    
       // Attach categories
       //加载方法列表,以及分类数据
       methodizeClass(cls, previously);
    
       return cls;
    }
    

    分类的本质

    在进入methodizeClass方法前我们先看一下分类的本质是什么。最核心的是被转换成了_category_t这个一个结构体。

    //定义一个LGPerson的分类,增加一个类方法和实例方法以及一个属性
    @interface LGPerson(AA)
    @property (nonatomic, copy) NSString *categoryNickName;
    
    - (void)lg_categoryInstanceMethod;
    + (void)lg_categoryClassMethod;
    @end
    
    @implementation LGPerson(AA)
    - (void)lg_categoryInstanceMethod{
    
    }
    + (void)lg_categoryClassMethod {
    
    }
    @end
    
    然后用 clang -rewrite-objc main.m -o main.cpp 看一下在编译阶段真正的实现
    
    //声明并初始化了一个类型为_category_t的全局静态变量
    static struct _category_t _OBJC_$_CATEGORY_LGPerson_$_AA __attribute__ ((used, section ("__DATA,__objc_const"))) = 
    {
       "LGPerson",
       0, // &OBJC_CLASS_$_LGPerson,
       (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_LGPerson_$_AA,
       (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_LGPerson_$_AA,
       0,
       (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_LGPerson_$_AA,
    };
    
    //看一下_category_t是什么
    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;//属性列表
    };
    
    static struct /*_method_list_t*/ {
       unsigned int entsize;  // sizeof(struct _objc_method)
       unsigned int method_count;
       struct _objc_method method_list[1];
    } _OBJC_$_CATEGORY_INSTANCE_METHODS_LGPerson_$_AA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
       sizeof(_objc_method),
       1,
       {{(struct objc_selector *)"lg_categoryInstanceMethod", "v16@0:8", (void *)_I_LGPerson_AA_lg_categoryInstanceMethod}}
    };
    
    static struct /*_method_list_t*/ {
       unsigned int entsize;  // sizeof(struct _objc_method)
       unsigned int method_count;
       struct _objc_method method_list[1];
    } _OBJC_$_CATEGORY_CLASS_METHODS_LGPerson_$_AA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
       sizeof(_objc_method),
       1,
       {{(struct objc_selector *)"lg_categoryClassMethod", "v16@0:8", (void *)_C_LGPerson_AA_lg_categoryClassMethod}}
    };
    
    static struct /*_prop_list_t*/ {
       unsigned int entsize;  // sizeof(struct _prop_t)
       unsigned int count_of_properties;
       struct _prop_t prop_list[1];
    } _OBJC_$_PROP_LIST_LGPerson_$_AA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
       sizeof(_prop_t),
       1,
       {{"categoryNickName","T@\"NSString\",C,N"}}
    };
    

    可以看到在底层分类被转换成了_category_t这个结构体,里面存储了所有的方法和属性,但属性没有get、set方法。

    方法的处理

    那接下来我们看一下methodizeClass中是如何处理类的方法以及分类方法的。主要是从ro中取出baseMethods,然后进行排序,如果需要处理分类(当分类中实现了load方法),则会把分类数据attach到整个方法列表中来。

    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();
       
       // Methodizing for the first time
       if (PrintConnecting) {
           _objc_inform("CLASS: methodizing class '%s' %s", 
                        cls->nameForLogging(), isMeta ? "(meta)" : "");
       }
    
       // Install methods and properties that the class implements itself.
       method_list_t *list = ro->baseMethods();
       if (list) {
           //对方法进行排序
           prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
           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);
    
    #if DEBUG
       // Debug: sanity-check all SELs; log method list contents
       for (const auto& meth : rw->methods()) {
           if (PrintConnecting) {
               _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', 
                            cls->nameForLogging(), sel_getName(meth.name));
           }
           ASSERT(sel_registerName(sel_getName(meth.name)) == meth.name); 
       }
    #endif
    }
    

    看一下attachToClass的实现,该方法主要是主类没有load方法,分类有load方法时进行分类方法的attach。

    void attachToClass(Class cls, Class previously, int flags)
       {
           runtimeLock.assertLocked();
           ASSERT((flags & ATTACH_CLASS) ||
                  (flags & ATTACH_METACLASS) ||
                  (flags & ATTACH_CLASS_AND_METACLASS));
           
           auto &map = get();
           auto it = map.find(previously);
           if (it != map.end()) {
               //只有主类没load,分类有load时条件才成立
               category_list &list = it->second;
               if (flags & ATTACH_CLASS_AND_METACLASS) {
                   int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
                   //处理类
                   attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
                   //处理元类
                   attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
               } else {
                   //只处理类
                   attachCategories(cls, list.array(), list.count(), flags);
               }
               map.erase(it);
           }
       }
    

    看一下最attachCategories实现,主要逻辑是先创建rwe,然后遍历所有的分类方法,然后一个一个attach到类的methodlist中来,并排好序

    static void attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                    int flags)
    {
       if (slowpath(PrintReplacedMethods)) {
           printReplacements(cls, cats_list, cats_count);
       }
       if (slowpath(PrintConnecting)) {
           _objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
                        cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
                        cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
       }
    
       constexpr uint32_t ATTACH_BUFSIZ = 64;
       method_list_t   *mlists[ATTACH_BUFSIZ];
       property_list_t *proplists[ATTACH_BUFSIZ];
       protocol_list_t *protolists[ATTACH_BUFSIZ];
    
       uint32_t mcount = 0;
       uint32_t propcount = 0;
       uint32_t protocount = 0;
       bool fromBundle = NO;
       bool isMeta = (flags & ATTACH_METACLASS);
       //创建rwe
       auto rwe = cls->data()->extAllocIfNeeded();
       for (uint32_t i = 0; i < cats_count; i++) {
           auto& entry = cats_list[i];
    
           method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
           if (mlist) {
               if (mcount == ATTACH_BUFSIZ) {
                   prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
                   rwe->methods.attachLists(mlists, mcount);
                   mcount = 0;
               }
               mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
               fromBundle |= entry.hi->isBundle();
           }
    
           property_list_t *proplist =
               entry.cat->propertiesForMeta(isMeta, entry.hi);
           if (proplist) {
               if (propcount == ATTACH_BUFSIZ) {
                   rwe->properties.attachLists(proplists, propcount);
                   propcount = 0;
               }
               proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
           }
    
           protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
           if (protolist) {
               if (protocount == ATTACH_BUFSIZ) {
                   rwe->protocols.attachLists(protolists, protocount);
                   protocount = 0;
               }
               protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
           }
       }
    
       if (mcount > 0) {
           prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
           rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
           if (flags & ATTACH_EXISTING) flushCaches(cls);
       }
    
       rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
    
       rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
    }
    

    看一下attachlists方法,该方法分三种情况,如果只有一个list,那整个方法列表将是一个单列表,如果从一个变多个list,那将重新分配内存就成二维数组,如果本身就是二维的继续往里面插,则进行内存拷贝和移动。对于新的list是放前面的,所以分类的方法在查找时先被找到。

    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;
                memmove(array()->lists + addedCount, array()->lists, 
                        oldCount * sizeof(array()->lists[0]));
                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]));
            }
        }
    

    其实跟踪attachCategories的调用情况,可以看到还有这样一条调用链路:
    load_images->loadAllCategories->load_categories_nolock->attachCategories
    那什么情况下会走到该流程中呢,和上面我们分析的分类方法的处理流程又有什么不同呢?
    其实主要是根据主类和分类是否有load方法,会有不同的逻辑处理,分4种情况:

    • 主类有load,分类有load,分类的加载就是load_images流程中,此时会产生rwe。
    • 主类有load,分类没load,主类进入非懒加载流程提交实现,在主类加载的时候会把主类中的方法和分类中的方法一并加载进来,所以此时不会产生rwe。
    • 主类没load,分类有load,在load_images方法的prepare_load_methods流程中会加载所有分类中的load方法,此时会调用主类的实现方法,而在其实现方法中会调用attachCategories加载分类。此时会产生rwe。
    • 主类没load,分类没load,主类的实现在第一次消息发送的时候,然后会把主类和分类中的方法都一次性加载进来,此时不会产生rwe。

    相关文章

      网友评论

          本文标题:OC类的加载

          本文链接:https://www.haomeiwen.com/subject/jbxawktx.html