// 统计objc_restartableRanges数组中location成员值不为空的task_restartable_range_t的数量 while (objc_restartableRanges[count].location) { count++; }
// 为当前任务注册一组可重启范围。Register a set of restartable ranges for the current task. kr = task_restartable_ranges_register(mach_task_self(), objc_restartableRanges, count); if (kr == KERN_SUCCESS) return; // 注册失败则停止运行 _objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)", kr, mach_error_string(kr)); #endif// HAVE_TASK_RESTARTABLE_RANGES }
/** * Process the given images which are being mapped(映射、加载) in by dyld. * Calls ABI-agnostic code after taking ABI-specific locks. * Locking: write-locks runtimeLock */ voidmap_images(unsigned count, constchar * const paths[], const struct mach_header * const mhdrs[]) { rwlock_writer_tlock(runtimeLock); returnmap_images_nolock(count, paths, mhdrs); }
// ⬇️⬇️⬇️ // Perform one-time runtime initialization that must be deferred until the executable itself is found. // 执行 one-time runtime initialization,必须推迟到找到可执行文件本身。 // 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. // 如果可执行文件不包含 Objective-C 代码但稍后动态加载 Objective-C,则该可执行文件可能不会出现在此 infoList 中。 if (firstTime) { // 初始化 selector 表并注册内部使用的 selectors。 sel_init(selrefCount); // ⬇️⬇️⬇️ 这里的 arr_init 函数超重要,可看到它内部做了三件事: // 1. 自动释放池的初始化(实际是在 TLS 中以 AUTORELEASE_POOL_KEY 为 KEY 写入 tls_dealloc 函数(自动释放池的销毁函数:内部所有 pages pop 并 free)) // 2. SideTablesMap 初始化,也可理解为 SideTables 的初始化(为 SideTables 这个静态全局变量开辟空间) // 3. AssociationsManager 的初始化,即为全局使用的关联对象表开辟空间 // void arr_init(void) // { // AutoreleasePoolPage::init(); // SideTablesMap.init(); // _objc_associations_init(); // } arr_init(); ...
// 这一段是在较低版本下 DYLD_MACOSX_VERSION_10_13 之前的版本中禁用 +initialize fork safety,大致看看即可 #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; unsignedlong 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
} // ⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️ 下面就来到了最核心的地方 // 以 header_info *hList[mhCount] 数组中收集到的 images 的 header_info 为参,直接进行 image 的读取 if (hCount > 0) { _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses); } // 把开始时初始化的静态局部变量 firstTime 置为 NO firstTime = NO; // ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️ // _read_images 看完再看下面的 loadImageFuncs 函数 // Call image load funcs after everything is set up. // 一切设置完毕后调用 image 加载函数。 for (auto func : loadImageFuncs) { for (uint32_t i = 0; i < mhCount; i++) { func(mhdrs[i]); } } }
// 类是元类 #define RO_META (1<<0) // 类是根类 #define RO_ROOT (1<<1) // 类有CXX构造/析构函数 #define RO_HAS_CXX_STRUCTORS (1<<2) // 类有实现load方法 // #define RO_HAS_LOAD_METHOD (1<<3) // 隐藏类 #define RO_HIDDEN (1<<4) // class has attribute(objc_exception): OBJC_EHTYPE_$_ThisClass is non-weak #define RO_EXCEPTION (1<<5) // class has ro field for Swift metadata initializer callback #define RO_HAS_SWIFT_INITIALIZER (1<<6) // 类使用ARC选项编译 #define RO_IS_ARC (1<<7) // 类有CXX析构函数,但没有CXX构造函数 #define RO_HAS_CXX_DTOR_ONLY (1<<8) // class is not ARC but has ARC-style weak ivar layout #define RO_HAS_WEAK_WITHOUT_ARC (1<<9) // 类禁止使用关联对象 #define RO_FORBIDS_ASSOCIATED_OBJECTS (1<<10)
// class is in an unloadable bundle - must never be set by compiler #define RO_FROM_BUNDLE (1<<29) // class is unrealized future class - must never be set by compiler #define RO_FUTURE (1<<30) // class is realized - must never be set by compiler #define RO_REALIZED (1<<31)
// OPTION( PrintConnecting, OBJC_PRINT_CLASS_SETUP, "log progress of class and category setup") // objc[26520]: CLASS: found 25031 classes during launch 在 objc-781 下在启动时有 25031 个类(包含所有的系统类和自定义类) 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 // isPreoptimized 如果我们有一个有效的优化共享缓存(valid optimized shared cache),则返回 YES。 // 然后是不管三目运算符返回的是 unoptimizedTotalClasses 还是 totalClasses,它都会和后面的 4 / 3 相乘, // 注意是 4 / 3 int namedClassesSize = (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3; // gdb_objc_realized_classes 是一张全局的哈希表,虽然名字中有 realized,但是它的名字其实是一个误称, // 实际上它存放的是不在 dyld shared cache 中的 class,无论该 class 是否 realized。 gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize); // 在 objc-781 下执行到这里时,会有如下打印: // objc[19881]: 0.04 ms: IMAGE TIMES: first time tasks // 这个过程花了 0.04 毫秒 ts.log("IMAGE TIMES: first time tasks"); }
// Discover classes. Fix up unresolved future classes. Mark bundle classes. // 发现 classes。修复 unresolved future classes。标记 bundle classes。
// Returns if any OS dylib has overridden its copy in the shared cache // // Exists in iPhoneOS 3.1 and later // Exists in Mac OS X 10.10 and later 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() // Image 已充分优化,我们无需调用 readClass() continue; }
for (i = 0; i < count; i++) { Class cls = (Class)classlist[i]; // 重点 ⚠️⚠️⚠️⚠️ 在这里:readClass。 // 我们留在下面单独分析。 Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
/* Return the id of the named class. 如果该类不存在,则返回一个未初始化的类结构,该结构将在类加载时使用。 */ Class objc_getFutureClass(constchar *name){ Class cls;
/* Class look_up_class(const char *name, bool includeUnconnected __attribute__((unused)), bool includeClassHandler __attribute__((unused))) // unconnected is OK,因为总有一天它会成为真正的class */ cls = look_up_class(name, YES, NO); if (cls) { if (PrintFuture) { _objc_inform("FUTURE: found %p already in use for %s", (void*)cls, name); } return cls; } // 还没有名为name的class或future class。做一个。 return _objc_allocateFutureClass(name); }
调用链向上追溯到 Class objc_getFutureClass,该函数并没有在 runtime 源代码中被调用到。而用于从 namedFutureClasses 哈希表中获取 future class 的popFutureClass(...) 函数是有间接通过readClass(...)函数被广泛调用。因此,构建 future class 的逻辑大多隐藏在 runtime 的内部实现中未公布,只有使用 future class 的逻辑是开源的。
2) future class的获取
popFutureNamedClass 用于从 futureNamedClasses 哈希表中弹出类名为name的 future class,这是获取全局记录的 future class 的唯一入口。
/* Removes the named class from the unrealized future class list, because it has been realized. * Returns nil if the name is not used by a future class. */ static Class popFutureNamedClass(constchar *name) { runtimeLock.assertLocked();
Class cls = nil;
if (future_named_class_map) { cls = (Class)NXMapKeyFreeingRemove(future_named_class_map, name); if (cls && NXCountMapTable(future_named_class_map) == 0) { NXFreeMapTable(future_named_class_map); future_named_class_map = nil; } }
/*********************************************************************** * readClass * Read a class and metaclass as written by a compiler. * Returns the new class pointer. This could be: * - cls * - nil (cls has a missing weak-linked superclass) * - something else (space for this class was reserved by a future class) * * Locking: runtimeLock acquired by map_images or objc_readClassPair **********************************************************************/ Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized) { constchar *mangledName = cls->nonlazyMangledName(); // 类的继承链上,存在既不是根类(RO_ROOT位为0)又没有超类的类,则为missingWeakSuperclass // 注意:这是唯一的向remappedClasses中添加nil值的入口 if (missingWeakSuperclass(cls)) { addRemappedClass(cls, nil); cls->setSuperclass(nil); return nil; } // 兼容旧版本libobjc的配置,可忽略 cls->fixupBackwardDeployingStableSwift();
Class replacing = nil; if (mangledName != nullptr) { if (Class newCls = popFutureNamedClass(mangledName)) { // 这个name已经被分配为future class,全局记录。 // 将cls的内容拷贝到newCls(也就是future class)中,保存future class的rw中的数据。将cls->data设置为rw->ro // 以cls为关键字将构建的newCls添加到全局记录的remappedClasses哈希表中
if (newCls->isAnySwift()) { _objc_fatal("Can't complete future class request for '%s' " "because the real class is too big.", cls->nameForLogging()); }
// Manually set address-discriminated ptrauthed fields // so that newCls gets the correct signatures. newCls->setSuperclass(cls->getSuperclass()); newCls->initIsa(cls->getIsa());
replacing = cls; cls = newCls; } } if (headerIsPreoptimized && !replacing) { // class list built in shared cache // 已存在该类名的named class ASSERT(mangledName == nullptr || getClassExceptSomeSwift(mangledName)); } else { if (mangledName) { // 一些Swift泛型类可以惰性地生成它们的名称 // 将类添加到 named classes addNamedClass(cls, mangledName, replacing); } else { Class meta = cls->ISA(); constclass_ro_t *metaRO = meta->bits.safe_ro(); ASSERT(metaRO->getNonMetaclass() && "Metaclass with lazy name must have a pointer to the corresponding nonmetaclass."); ASSERT(metaRO->getNonMetaclass() == cls && "Metaclass nonmetaclass pointer must equal the original class."); } // 将类添加到 allocated classes addClassTableEntry(cls); }
// for future reference: shared cache never contains MH_BUNDLEs // 设置RO_FROM_BUNDLE位 if (headerIsBundle) { cls->data()->flags |= RO_FROM_BUNDLE; cls->ISA()->data()->flags |= RO_FROM_BUNDLE; } return cls; }
4) future class小结
从上文readClass(...)代码if (Class newCls = popFutureNamedClass(mangledName))分支内free((void *)old_ro)语句,得出在cls映射到newCls过程中,完全丢弃了 future class 的ro数据。最后,结合以上所有代码,可以归纳以下结论:
Future class 类的有效数据实际上仅有:类名和rw。rw中的数据作用也非常少,仅使用flags的RO_FUTURE(实际上就是RW_FUTURE)标记类是 future class;
Future class 的作用是为指定类名的类,提前分配好内存空间,调用readClass(...)函数读取类时,才正式写入类的数据。 Future class 是用于支持类的懒加载机制;
// Fix up remapped classes // Class list and nonlazy class list remain unremapped. // Class list 和 nonlazy class list 仍未映射。 // Class refs and super refs are remapped for message dispatching. // Class refs 和 super refs 被重新映射为消息调度。 // 主要是修复重映射 classes,!noClassesRemapped() 在这里为 false,所以一般走不进来, // 将未映射 class 和 super class 重映射,被 remap 的类都是非懒加载的类 if (!noClassesRemapped()) { for (EACH_HEADER) { // GETSECT(_getObjc2ClassRefs, Class, "__objc_classrefs"); // 获取 __objc_classrefs 区中的类引用 Class *classrefs = _getObjc2ClassRefs(hi, &count); // 遍历 classrefs 中的类引用,如果类引用已被重新分配或者是被忽略的弱链接类, // 就将该类引用重新赋值为从重映射类表中取出新类 for (i = 0; i < count; i++) { // Fix up a class ref, in case the class referenced has been reallocated or is an ignored weak-linked class. // 修复 class ref,以防所引用的类已 reallocated 或 is an ignored weak-linked class。 remapClassRef(&classrefs[i]); } // fixme why doesn't test future1 catch the absence of this? // GETSECT(_getObjc2SuperRefs, Class, "__objc_superrefs"); // 获取 __objc_superrefs 区中的父类引用 classrefs = _getObjc2SuperRefs(hi, &count); for (i = 0; i < count; i++) { remapClassRef(&classrefs[i]); } } }
综合上面代码的详细注释,可知cls重映射到newCls后,newCls的数据保留了cls中的superclass、cache成员,但是bits中指向class_rw_t结构体地址的位域(FAST_DATA_MASK)指向了新的class_rw_t结构体。该结构体的ro指针指向cls->data()所指向的内存空间中保存的class_ro_t结构体,其他数据则是直接沿用 从namedFutureClasses哈希表中弹出的 future class 的class_rw_t结构体(通过future class 的data()方法返回)中数据。
if (remapped_class_map) return remapped_class_map; if (!create) return nil;
// remapped_class_map is big enough to hold CF’s classes and a few others INIT_ONCE_PTR(remapped_class_map, NXCreateMapTable(NXPtrValueMapPrototype, 32), NXFreeMapTable(v));
return remapped_class_map; }
// 将oldcls重映射得到的newcls,以oldcls为关键字插入到remappedClasses哈希表中 // 注意:从代码透露出来的信息是,remappedClasses中只保存 future class 重映射的类 staticvoidaddRemappedClass(Class oldcls, Class newcls) { runtimeLock.assertLocked();
if (PrintFuture) { _objc_inform("FUTURE: using %p instead of %p for %s", (void*)newcls, (void*)oldcls, oldcls->nameForLogging()); }
void *old; old = NXMapInsert(remappedClasses(YES), oldcls, newcls); assert(!old); }
// 获取cls的重映射类 // 注意:当remappedClasses为空或哈希表中不存在`cls`关键字,是返回`cls`本身,否则返回`cls`重映射后的类 static Class remapClass(Class cls) { runtimeLock.assertLocked();
// Skip reading protocols if this is an image from the shared cache and we support roots // 如果这是来自 shared cache 的 image 并且我们 support roots,则跳过 reading protocols // 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 // 启动后,我们确实需要遍历协议,因为 shared cache 中的协议用 isCanonical() 标记,如果选择某些非共享缓存二进制文件作为规范定义,则可能不是这样 if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) { if (PrintProtocols) { _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s", hi->fname()); } continue; }
bool isBundle = hi->isBundle(); // GETSECT(_getObjc2ProtocolList, protocol_t * const, "__objc_protolist"); // 获取 hi 的 __objc_protolist 区下的 protocol_t protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count); for (i = 0; i < count; i++) { // Read a protocol as written by a compiler. readProtocol(protolist[i], cls, protocol_map, isPreoptimized, isBundle); } }
// Fix up @protocol references // Preoptimized images may have the right answer already but we don't know for sure. // Preoptimized images 可能已经有了正确的答案,但我们不确定。 for (EACH_HEADER) { // At launch time, we know preoptimized image refs are pointing at the shared cache definition of a protocol. // 在启动时,我们知道 preoptimized image refs 指向协议的 shared cache 定义。 // We can skip the check on launch, but have to visit @protocol refs for shared cache images loaded later. // 我们可以跳过启动时的检查,但必须访问 @protocol refs 以获取稍后加载的 shared cache images。 if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized()) continue; // GETSECT(_getObjc2ProtocolRefs, protocol_t *, "__objc_protorefs"); // 获取 hi 的 __objc_protorefs 区的 protocol_t protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count); for (i = 0; i < count; i++) { // Fix up a protocol ref, in case the protocol referenced has been reallocated. // 修复 protocol ref,以防 protocol referenced 已重新分配。 remapProtocolRef(&protolist[i]); } }
// 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 // 发现类别。仅在完成 initial category attachment 后才执行此操作。 // 对于启动时出现的categories,discovery被推迟到_dyld_objc_notify_register调用完成后的第一个load_images调用后。 // didInitialAttachCategories 是一个静态全局变量,默认是 false, // static bool didInitialAttachCategories = false; 在load_images()函数体中,才会置为true
// 所以,这里 if 里面的 category 数据加载是不会执行的。
if (didInitialAttachCategories) { for (EACH_HEADER) { load_categories_nolock(hi); } }
/*********************************************************************** * methodizeClass * Fixes up cls's method list, protocol list, and property list. * Attaches any outstanding categories. * Locking: runtimeLock must be held by the caller **********************************************************************/ staticvoidmethodizeClass(Class cls, Class previously) { runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass(); auto rw = cls->data(); auto ro = rw->ro(); auto rwe = rw->ext();
// 安装类自己实现的方法和属性。Install methods and properties that the class implements itself. // 将ro中的基本方法列表添加到rw的方法列表中 method_list_t *list = ro->baseMethods(); if (list) { prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr); if (rwe) rwe->methods.attachLists(&list, 1); } // 将ro中的属性列表添加到rw的属性列表中 property_list_t *proplist = ro->baseProperties; if (rwe && proplist) { rwe->properties.attachLists(&proplist, 1); } // 将ro中的协议列表添加到rw的协议列表中 protocol_list_t *protolist = ro->baseProtocols; if (rwe && protolist) { rwe->protocols.attachLists(&protolist, 1); }
// Realize newly-resolved future classes, in case CF manipulates them // 实现 newly-resolved future classes,以防 CF 操作它们 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); // 将此类及其所有子类标记为需要原始 isa 指针 cls->setInstancesRequireRawIsaRecursively(false/*inherited*/); } free(resolvedFutureClasses); }
if (mcount > 0) { prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle, __func__); rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount); if (flags & ATTACH_EXISTING) { flushCaches(cls, __func__, [](Class c){ // constant caches have been dealt with in prepareMethodLists // if the class still is constant here, it's fine to keep return !c->cache.isConstantOptimizedCache(); }); } }
/* * Class cls has just become connected. Schedule it for +load if it implements a +load method. */ voidadd_class_to_loadable_list(Class cls) { IMP method;
loadMethodLock.assertLocked();
// 1. 从 class 中获取 load 方法 method = cls->getLoadMethod(); if (!method) return; // Don't bother if cls has no +load method if (PrintLoading) { _objc_inform("LOAD: class '%s' scheduled for +load", cls->nameForLogging()); } // 2. 判断当前 loadable_classes 这个数组是否已经被全部占用 if (loadable_classes_used == loadable_classes_allocated) { loadable_classes_allocated = loadable_classes_allocated*2 + 16; // 3. 在当前数组的基础上扩大数组的大小:realloc loadable_classes = (struct loadable_class *) realloc(loadable_classes, loadable_classes_allocated * sizeof(struct loadable_class)); } // 4. 把传入的 class 以及对应的方法的实现IMP加到列表中 loadable_classes[loadable_classes_used].cls = cls; loadable_classes[loadable_classes_used].method = method; loadable_classes_used++; }
/** * Call all pending class and category +load methods. * Class +load methods are called superclass-first. * Category +load methods are not called until after the parent class's +load. */ voidcall_load_methods(void) { staticbool loading = NO; bool more_categories; // 加锁 loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job. // 重入调用什么都不做;最外层的调用将完成工作。 // 如果正在 loading 则 return, // 保证当前 +load 方法同时只有一次被调用 if (loading) return; loading = YES;
// 3. Run more +loads if there are classes OR more untried categories } while (loadable_classes_used > 0 || more_categories); // 如果 loadable_classes_used 大于 0,或者有更多分类需要调用 +load,则循环继续。(一般 loadable_classes_used 到这里基本就是 0 了) // 自动释放池进行 pop objc_autoreleasePoolPop(pool);
类完成 class realizing 后,还需要执行类及分类中的load()方法,最后在程序运行过程中第一次调用类的方法时(实现逻辑在IMP lookUpImpOrForward(...)函数中)触发isInitialized()检查,若未初始化,则需要先执行类的initialize()方法。至此,类正式加载完成。
注意:最后的 class initializing 严格意义上应该不属于类的加载过程,可以将其归为独立的类初始化阶段。类的加载在load()方法执行后就算是完成了。
/* * Process the given image which is about to be unmapped by dyld. */ void unmap_image(constchar *path __unused, const struct mach_header *mh) { recursive_mutex_locker_tlock(loadMethodLock); mutex_locker_tlock2(runtimeLock); unmap_image_nolock(mh); }
header_info *hi; // Find the runtime's header_info struct for the image for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) { if (hi->mhdr() == (const headerType *)mh) { break; } }
// Unload unattached categories and categories waiting for +load.
// Ignore __objc_catlist2. We don't support unloading Swift // and we never will. category_t * const *catlist = hi->catlist(&count); for (i = 0; i < count; i++) { category_t *cat = catlist[i]; Class cls = remapClass(cat->cls); if (!cls) continue; // category for ignored weak-linked class
// fixme for MH_DYLIB cat's class may have been unloaded already
// unattached list objc::unattachedCategories.eraseCategoryForClass(cat, cls);
// Gather classes from both __DATA,__objc_clslist // and __DATA,__objc_nlclslist. arclite's hack puts a class in the latter // only, and we need to unload that class if we unload an arclite image.