dyld中的objc_init、map_images、load_images
一 我们要分析的流程阶段:
参考这篇博客:https://juejin.cn/post/7098001140553351198#heading-1
dyld在main函数之前会递归加载动态库,此时会间接调用到_objc_init, (在libSystem
动态库被加载的时候,它的子libObjc
会调用_objc_init
进行objc
的初始化)以及_dyld_objc_notify_register 注册的三个方法。接下来,我们重点基于这三个方法展开介绍。
二、_objc_init解析
我们可以在objc的源码中找到该函数,并对该函数的具体部分解析:
void _objc_init(void) { static bool initialized = false; if (initialized) return; initialized = true; // fixme defer initialization until an objc-using image is found? //环境变量 environ_init(); //绑定线程析构函数 tls_init(); //静态构造函数 static_init(); //runtime准备,创建2张表 runtime_init(); //异常初始化 exception_init(); #if __OBJC2__ //缓存 cache_t::init(); #endif //macos专有 _imp_implementationWithBlock_init(); _dyld_objc_notify_register(&map_images, load_images, unmap_image); #if __OBJC2__ didCallDyldNotifyRegister = true; #endif }
2.1 environ_init
其中environ_init是读取环境变量的一些配置信息,环境变量可以在在项目的Edit Scheme -> Run-> Argments ->Enviroment Variables中配置。
在这里,我们将一些以后可能会碰到的环境变量列举如下:
2.2 tls_init
这里的tls是Thread Local Store的缩写,即线程局部存储,他主要用在多线程中,存储和维护一些线程相关的数据,存储的数据会被关关联到当前线程中,不需要锁来维护,在下面这张图片的最后一句代码中是为线程添加析构函数,调用destoryspecific函数。
2.3 static_init
他的主要作用是:找到objc库中的所有初始化方法,遍历调用,这里需要注意的是objc库中的静态构造函数早于load方法,load方法又早于我们自己写的静态构造函数。
2.4 runtime_init
这里面其实是初始化两张表(分类表和类表),以备后边加载类使用。
2.5 其他的一些init方法
exception_init :初始化异常捕捉相关
cache_t::init : 初始化缓存相关
_imp_implementationWithBlock_init : MacOS中,让dyld去加载libobjc-trampolines.dylib这个库。
在上篇文章中,我们最后探索到了dyld会调用 map_images 和 load_images 来对image进行初始化的操作,接下来,我们分别从三、四中来具体了解一些这两个函数。
三、load_images解析
我们先来看一下load_images:
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 { // 找到load方法 mutex_locker_t lock2(runtimeLock); prepare_load_methods((const headerType *)mh); } // Call +load methods (without runtimeLock - re-entrant) //调用load方法 call_load_methods(); }
3.1 loadAllCategories() 加载分类:
在loadAllCategories() 中调用了 load_categories_nolock()函数,在最后将所有分类添加到 runtime_init 中初始化的unattachedCategories 表中
static void load_categories_nolock(header_info *hi) { bool hasClassProperties = hi->info()->hasCategoryClassProperties(); size_t count; auto processCatlist = [&](category_t * const *catlist) { for (unsigned i = 0; i < count; i++) { category_t *cat = catlist[i]; Class cls = remapClass(cat->cls); locstamped_category_t lc{cat, hi}; if (!cls) { // Category's target class is missing (probably weak-linked). // Ignore the category. if (PrintConnecting) { _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with " "missing weak-linked target class", cat->name, cat); } continue; } // Process this category. if (cls->isStubClass()) { // Stub classes are never realized. Stub classes // don't know their metaclass until they're // initialized, so we have to add categories with // class methods or properties to the stub itself. // methodizeClass() will find them and add them to // the metaclass as appropriate. if (cat->instanceMethods || cat->protocols || cat->instanceProperties || cat->classMethods || cat->protocols || (hasClassProperties && cat->_classProperties)) { objc::unattachedCategories.addForClass(lc, cls); } } else { // First, register the category with its target class. // Then, rebuild the class's method lists (etc) if // the class is realized. if (cat->instanceMethods || cat->protocols || cat->instanceProperties) { if (cls->isRealized()) { attachCategories(cls, &lc, 1, ATTACH_EXISTING); } else { //这个表就是分类表 objc::unattachedCategories.addForClass(lc, cls); } } if (cat->classMethods || cat->protocols || (hasClassProperties && cat->_classProperties)) { if (cls->ISA()->isRealized()) { attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS); } else { // runtime_init的时候创建,第一部分有讲到 objc::unattachedCategories.addForClass(lc, cls->ISA()); } } } } }; processCatlist(hi->catlist(&count)); processCatlist(hi->catlist2(&count)); }
3.2 prepare_load_methods() 调用load方法的准备(查找load方法)
从所有的非懒加载类、非懒加载分类中找出load
方法,这里需要明白的是只有非懒加载类、非懒加载分类中才会自己实现load
方法
void prepare_load_methods(const headerType *mhdr) { size_t count, i;\ runtimeLock.assertLocked(); //获取所有非懒加载类 classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count); for (i = 0; i < count; i++) { schedule_class_load(remapClass(classlist[i])); } //获取所有非懒加载分类 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; // category for ignored weak-linked class //swift没有load方法 if (cls->isSwiftStable()) { _objc_fatal("Swift class extensions and categories on Swift " "classes are not allowed to have +load methods"); } //实现类 realizeClassWithoutSwift(cls, nil); ASSERT(cls->ISA()->isRealized()); add_category_to_loadable_list(cat); }
3.3 call_load_methods() 调用load方法
void call_load_methods(void) { static bool loading = NO; bool more_categories; //加锁:线程安全 loadMethodLock.assertLocked(); // Re-entrant calls do nothing; the outermost call will finish the job. if (loading) return; loading = YES; void *pool = objc_autoreleasePoolPush(); do { // 1. Repeatedly call class +loads until there aren't any more while (loadable_classes_used > 0) { call_class_loads(); } // 2. Call category +loads ONCE more_categories = call_category_loads(); // 3. Run more +loads if there are classes OR more untried categories } while (loadable_classes_used > 0 || more_categories); objc_autoreleasePoolPop(pool); loading = NO; }
这里我们重点关注一下call_class_loads()方法
static void call_class_loads(void) { int i; // Detach current loadable list. struct loadable_class *classes = loadable_classes; int used = loadable_classes_used; loadable_classes = nil; loadable_classes_allocated = 0; loadable_classes_used = 0; // Call all +loads for the detached list. for (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()); } //⚠️直接函数调用,没有msg_send (*load_method)(cls, @selector(load)); } // Destroy the detached list. if (classes) free(classes); }
/*********************************************************************** * call_category_loads * Call some pending category +load methods. * The parent class of the +load-implementing categories has all of * its categories attached, in case some are lazily waiting for +initalize. * Don't call +load unless the parent class is connected. * If new categories become loadable, +load is NOT called, and they * are added to the end of the loadable list, and we return TRUE. * Return FALSE if no new categories became loadable. * * Called only by call_load_methods(). **********************************************************************/ static bool call_category_loads(void) { int i, shift; bool new_categories_added = NO; // Detach current loadable list. struct loadable_category *cats = loadable_categories; int used = loadable_categories_used; int allocated = loadable_categories_allocated; loadable_categories = nil; loadable_categories_allocated = 0; loadable_categories_used = 0; // Call all +loads for the detached list. for (i = 0; i < used; i++) { Category cat = cats[i].cat; load_method_t load_method = (load_method_t)cats[i].method; Class cls; if (!cat) continue; cls = _category_getClass(cat); if (cls && cls->isLoadable()) { if (PrintLoading) { _objc_inform("LOAD: +[%s(%s) load]\n", cls->nameForLogging(), _category_getName(cat)); } (*load_method)(cls, @selector(load)); cats[i].cat = nil; } } // Compact detached list (order-preserving) shift = 0; for (i = 0; i < used; i++) { if (cats[i].cat) { cats[i-shift] = cats[i]; } else { shift++; } } used -= shift; // Copy any new +load candidates from the new list to the detached list. new_categories_added = (loadable_categories_used > 0); for (i = 0; i < loadable_categories_used; i++) { if (used == allocated) { allocated = allocated*2 + 16; cats = (struct loadable_category *) realloc(cats, allocated * sizeof(struct loadable_category)); } cats[used++] = loadable_categories[i]; } // Destroy the new list. if (loadable_categories) free(loadable_categories); // Reattach the (now augmented) detached list. // But if there's nothing left to load, destroy the list. if (used) { loadable_categories = cats; loadable_categories_used = used; loadable_categories_allocated = allocated; } else { if (cats) free(cats); loadable_categories = nil; loadable_categories_used = 0; loadable_categories_allocated = 0; } if (PrintLoading) { if (loadable_categories_used != 0) { _objc_inform("LOAD: %d categories still waiting for +load\n", loadable_categories_used); } } return new_categories_added; }
通过对以上load_images的了解。我们总结出来以下几点:(load_images就是执行类和分类中的load方法)
1. +load的加载和调用是线程安全的 2.重写+load不需要在方法中重写[super load] 因为系统会自动帮我们确定好父类的load,同样的,如果一个类未实现load方法,也不会调用父类的load方法 3.类的+load被保存到了结构体指针数组中,保存了cls(类对象)和method 4.所有分类的+load同样也被保存到了结构体指针数组中,保存了cat(用它来获取cls的)和method 5.+load的调用顺序为父类->子类->分类 6.如果同一个类的分类中都实现+load,分类中的全部+load方法都会被调用,但是调用的顺序和编译的顺序有关
7.当有多个不同的类的时候,每个类load 执行顺序与其在编译出现的顺序一致
8.+load都是直接调用,而非消息发送
9. runtime 在load加载和调用环境中,更多的作用是获取类对象cls
四、map_images解析
我们看一下 map_images_nolock 函数,在这些函数中我们可以发现,该方法遍历了所有动态库的mach-header,生成了相应的header_info 并放进hlist数组中,与此同时计算出class的总数totalClasses。随后调用了_read_images读取所有的动态度信息。
/*********************************************************************** * map_images_nolock * Process the given images which are being mapped in by dyld. * All class registration and fixups are performed (or deferred pending * discovery of missing superclasses etc), and +load methods are called. * * info[] is in bottom-up order i.e. libobjc will be earlier in the * array than any library that links to libobjc. * * Locking: loadMethodLock(old) or runtimeLock(new) acquired by map_images. **********************************************************************/ #if __OBJC2__ #include "objc-file.h" #else #include "objc-file-old.h" #endif 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(); } //如果添加OBJC_PRINT_IMAGES环境,打印镜像数量 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]; //通过 初始化header_info,header_info是一个链表,目前使用链表的目的还未知,已经使用数组存储 //并且计算classed的数量 auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses); if (!hi) { // no objc data in this entry continue; } //如果是可执行文件(我们的代码生成的)MH_EXECUTE就是我们主工程的代码 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 if ( !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 } //把hi存储起来 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_init(selrefCount); // 初始化操作 3件事 // 1.自动释放池 AutoreleasePoolPage的初始化 // 2.SideTablesMap初始化 // 3.全局关联对象表的初始化 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_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. for (auto func : loadImageFuncs) { for (uint32_t i = 0; i < mhCount; i++) { func(mhdrs[i]); } } }
4.1 _read_images解析:我们按照log函数的打印输出将_read_images分段解析
4.1.1:这里初始化一些局部变量,并加锁
/*********************************************************************** * _read_images * Perform initial processing of the headers in the linked * list beginning with headerList. * * Called by: map_images_nolock * * Locking: runtimeLock acquired by map_images **********************************************************************/ 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; //环境变量设置 OBJC_PRINT_IMAGE_TIMES = YES 会打印ts.log的内容 TimeLogger ts(PrintImageTimes); // 加锁 runtimeLock.assertLocked();
4.1.2:这部分代码以静态变量doneOnce控制,只执行一次,主要作用是加载所有类到类的gdb_objc_realized_classes表中
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 //... 不影响整体理解,已删除 # endif #endif // TaggedPoint的一些初始化 if (DisableTaggedPointers) { disableTaggedPointers(); } initializeTaggedPointerObfuscator(); // OBJC_PRINT_CLASS_SETUP设置,可以答应class数量(系统+自定义) 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 是一张全局的哈希表 // 实际上它存放的是除了 dyld shared cache 中的 class,无论该 class 是否 realized。 gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize); ts.log("IMAGE TIMES: first time tasks"); }
4.1.3: 将SEL注册到nameSelectors表,并修复函数指针(修复是一种rebase操作,将虚拟内存映射到物理内存)
// Fix up @selector references // Note this has to be before anyone uses a method list, as relative method // lists point to selRefs, and assume they are already fixed up (uniqued). static size_t UnfixedSelectors; { mutex_locker_t lock(selLock); for (EACH_HEADER) { if (hi->hasPreoptimizedSelectors()) continue; bool isBundle = hi->isBundle(); //取出__objc_selrefs的函数 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); //进行修正(fix up)如果地址发生变化,使用函数表中的地址 if (sels[i] != sel) { sels[i] = sel; } } } } ts.log("IMAGE TIMES: fix up selector references");
4.1.4:注册类,重映射类,这里有一个readClass,我们找到其所对应的实现中的addClassTableEntry,这里的操作是将类添加到allocatedClasses这个表中,那么这里的表实在runtime_init的时候初始化的。
// 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");
4.1.5 部分消息重映射(这部分是为适配低版本,可以忽略)
#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
4.1.6 将protocols添加到protocols_map中,并重映射(加载协议,并修正协议)
// 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) { 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 && 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");
4.1.7 加载分类:注意:只有当执行过load_images的时候,这里才会遍历load_catagories_nolock。
// 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 //全局变量,load_images 的时候设置为YES 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()
4.1.8 初始化非懒加载类
// Realize non-lazy classes (for +load methods and static instances) for (EACH_HEADER) { classref_t const *classlist = hi->nlclslist(&count); for (i = 0; i < count; i++) { Class cls = remapClass(classlist[i]); if (!cls) continue; //把类放进allocatedClasses 这个表中,该表在runtime_init的时候初始化 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"); } //加载类(非swift类) realizeClassWithoutSwift(cls, nil); cls->setInstancesRequireRawIsaRecursively(false/*inherited*/); } free(resolvedFutureClasses); } ts.log("IMAGE TIMES: realize future classes");
在 realizeClassWithoutSwift(懒加载和非懒加载类的必经函数之路) 中,对类的rw进行了操作:从以下代码可以看出rw是运行时创建的,而ro是在编译的时候创建的。
//给rw开辟内存空间,然后将ro的数据“拷⻉”到rw里面。
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro); rw->flags = RW_REALIZED|RW_REALIZING|isMeta; cls->setData(rw); //递归调用realizeClassWithoutSwift,对父类和元类进行初始化 supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil); //设置父类,isa指针的初始化
cls->setSuperclass(supercls); cls->initClassIsa(metacls);
//处理分类
methodizeClass (cls,previously)//rwe的操作
4.1.8 其余的打印操作,主要是设置环境变量,辅助打印debug
// OBJC_DEBUG_NONFRAGILE_IVARS 设置 if (DebugNonFragileIvars) { realizeAllClasses(); } // Print preoptimization statistics // OBJC_PRINT_PREOPTIMIZATION 设置YES打印 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 }
4.2 总结read_images
1.加载所有类到类的gdb_objc_realized_classes表中 2.对所有类做重映射 3.将所有SEL都注册到namedSelectors表中 4.修复函数指针遗留 5.将所有Protocol都添加到protocol_map表中 6.对所有Protocol做重映射 7.初始化所有非懒加载类,进行rw、ro等操作 8.遍历已标记的懒加载类,并做初始化操作 9.处理所有Category,包括Class和Meta Class 10.初始化所有未初始化的类
五、小补充
5.1 懒加载类与非懒加载类 的区别:当前类是否实现 load 方法
5.2 realizeClassWithoutSwift在懒加载类调用的时候是在该类被实现的时候才会调用(懒加载在第一次接收到消息的时候才开始加载),而非懒加载类则是在load方法时调用。
5.2 unmap_images 是析构所有的类