dyld
dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统一个重要组成部分,系统加载、链接mach-O文件,就是由dyld负责的,而且它是开源的,可以通过苹果官网下载它的源码来阅读理解它的工作原理,通过它可以了解系统加载动态库的更多细节;
dyld的加载流程从_dyld_start开始
-> 一系列流程
-> libSystem
-> libdispatch
-> objc中的_objc_init
,_objc_init
又调用了dyld
中实现的_dyld_objc_notify_register
注册回调函数,调用dyld
中的_dyld_objc_notify_register
的时候,又调用了map_images
函数、loadImages
函数,于是就把objc
和dyld
进行了关联;
_objc_init
首先看一下_objc_init入口函数:
void _objc_init(void) { static bool initialized = false; if (initialized) return; initialized = true; // 读取环境变量 environ_init(); //线程的绑定 tls_init(); //调用c++静态构造函数 static_init(); //主要是对unattachedCategories和allocatedClasses的初始化; runtime_init(); //异常处理 exception_init(); //缓存处理 cache_t::init(); //某些进程的特殊处理 _imp_implementationWithBlock_init(); // 注册回调函数 _dyld_objc_notify_register(&map_images, load_images, unmap_image); //标记已经加载完成 didCallDyldNotifyRegister = true; }
map_images
在_objc_init中通过_dyld_objc_notify_register函数,内部又调用了map_images、load_images和unmap_image;看一下map_images函数: 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); }
_read_images
接下来看_read_images函数: void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses) { //glt: 只会执行1次的代码加载 if (!doneOnce) { ... } //glt: 修复预编译阶段的@selector不相同(SEL的名称或者地址不同)的问题 //glt: 会对比Macho里面读取的sel和dyld加载的(sels[索引])sel对比,最终以Mach-o里面的为准 { ... } ts.log("IMAGE TIMES: fix up selector references"); //glt: 修复预编译阶段类的不同,对比Mach-o里面的类列表和readClass里面的类,最终以readClass为准 for (EACH_HEADER) { for (i = 0; i < count; i++) { //cls这里只能获取到地址 Class cls = (Class)classlist[i]; //调用addNamedClass后,有了类名了 Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized); ... } } ts.log("IMAGE TIMES: discover classes"); //glt: 修复重映射一些没有被镜像文件加载进来的类 if (!noClassesRemapped()) { ... } ts.log("IMAGE TIMES: remap classes"); //glt: 修复旧objc_msgSend_fixup调用(imp重指向) for (EACH_HEADER) { ... } ts.log("IMAGE TIMES: fix up objc_msgSend_fixup"); //glt: 查询并修复协议 // Discover protocols. Fix up protocol refs. for (EACH_HEADER) { ... } ts.log("IMAGE TIMES: discover protocols"); //glt: @protocol引用修复,修复没有被加载的协议 for (EACH_HEADER) { ... } ts.log("IMAGE TIMES: fix up @protocol references"); //glt: 仅在完成初始化分类后才执行此操作。对于启动时出现的分类,被推迟到_dyld_objc_notify_register 调用完成后的第一个load_images调用,即loadAllCategories(); if (didInitialAttachCategories) { ... } ts.log("IMAGE TIMES: discover categories"); // 处理非懒加载类 (实现了load方法) for (EACH_HEADER) { //从Mach-O中读出所有的类列表nlclslist -> _getObjc2NonlazyClassList -> __objc_nlclslist classref_t const *classlist = hi->nlclslist(&count); for (i = 0; i < count; i++) { Class cls = remapClass(classlist[i]); if (!cls) continue; //遍历classlist将class重新映射,得到的新class和metaClass插入类表中 addClassTableEntry(cls); realizeClassWithoutSwift(cls, nil); } } ts.log("IMAGE TIMES: realize non-lazy classes"); // 优化没有被处理的类 if (resolvedFutureClasses) { ... } ts.log("IMAGE TIMES: realize future classes"); if (DebugNonFragileIvars) { realizeAllClasses(); } }
map_images_nolock
map_images内部又调用了map_images_nolock: void map_images_nolock(unsigned mhCount, const char * const mhPaths[], const struct mach_header * const mhdrs[]) { // 执行首次初始化,需再普通库初始化前调用 if (firstTime) { preopt_init(); } // Find all images with Objective-C metadata. hCount = 0; // Count classes. Size various table based on the total. int totalClasses = 0; int unoptimizedTotalClasses = 0; //块处理,给hCount赋值 { ... } //执行一次性的运行时初始化,该初始化必须延迟到找到可执行文件本身,如果可执行文件不包含Objective-C代码,但后来动态加载了Objective-C,在infoList中可能不存在可执行文件; if (firstTime) { ... } if (hCount > 0) { //读取镜像文件 _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses); } firstTime = NO; //执行完之后调用镜像文件load函数 for (auto func : loadImageFuncs) { ... } }
主要做了以下几件事:
- 只会执行1次的代码加载
- 修复预编译阶段的@selector不相同(SEL的名称或者地址不同)的问题
- 修复预编译阶段的@selector不相同(SEL的名称或者地址不同)的问题,会对比Macho里面读取的sel和dyld加载的(sels[索引])sel对比,最终以Mach-o里面的为准
- 修复预编译阶段类的不同,内部调用了readClass,对比Mach-o里面的类列表和readClass里面的类,最终以readClass为准;
- 修复重映射一些没有被镜像文件加载进来的类
- 修复旧objc_msgSend_fixup调用(imp重指向)
- 查询并修复协议
- @protocol引用修复,修复没有被加载的协议
- 仅在完成初始化分类后才执行此操作,对于启动时出现的分类,被推迟到_dyld_objc_notify_register 调用完成后的第一个load_images调用,即loadAllCategories()
- 处理非懒加载类 (实现了load方法),内部会调用realizeClassWithoutSwift
- 优化没有被处理的类
readClass
接下来调用readClass,读取类信息:
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized) { //获取类名 const char *mangledName = cls->nonlazyMangledName(); if (missingWeakSuperclass(cls)) { ... } cls->fixupBackwardDeployingStableSwift(); Class replacing = nil; //移除现在不实现,将来才实现的类,注意这里面对rw的赋值并不是真正的为每个类的cls赋值 if (mangledName != nullptr) { if (Class newCls = popFutureNamedClass(mangledName)) { ... } } if (headerIsPreoptimized && !replacing) { ASSERT(mangledName == nullptr || getClassExceptSomeSwift(mangledName)); } else { if (mangledName) {//将类名绑定到数据表中 addNamedClass(cls, mangledName, replacing); } else { Class meta = cls->ISA(); const class_ro_t *metaRO = meta->bits.safe_ro(); } //加入到需要开辟内存的类的表 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; }
通过上述实现了把类和类名都加入到对应的表中,类中的属性、方法、协议等信息并未在此处理,经调试发现,无论是懒加载类还是非懒加载类都会执行realizeClassWithoutSwift函数,接下来我们直接看realizeClassWithoutSwift函数
- 懒加载类为没有实现load方法的类
- 非懒加载类为实现了load方法的类
realizeClassWithoutSwift static Class realizeClassWithoutSwift(Class cls, Class previously) { if (cls->isRealized()) { //内部会通过父类和元类递归调用此方法来设置,可以通过isRealized来区分,如果已经实现了防止持续递归 return cls; } const char *mangledName = cls->mangledName(); //从Mach- O里面读取到cls->data(),强制转换成class_ro_t结构 auto ro = (const class_ro_t *)cls->data(); auto isMeta = ro->flags & RO_META; if (ro->flags & RO_FUTURE) { // 元类、无需创建直接赋值 rw = cls->data(); ro = cls->data()->ro(); ASSERT(!isMeta); cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE); } else { // 普通类、先创建 rw = objc::zalloc<class_rw_t>(); //内部会先判断是否有rwe,如果有就设置rwe,否则设置给ro rw->set_ro(ro); rw->flags = RW_REALIZED|RW_REALIZING|isMeta; cls->setData(rw); } cls->cache.initializeToEmptyOrPreoptimizedInDisguise(); //设置bit if (isMeta) cls->cache.setBit(FAST_CACHE_META); // 为类选择一个索引, 如果没有索引则设置cls->instancesRequireRawIsa cls->chooseClassArrayIndex(); //递归实现为父类赋值 supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil); //递归实现为元类赋值 metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil); if (isMeta) { // 元类不需要non pointer ISA cls->setInstancesRequireRawIsa(); } else {... //设置isa } // 更新父类和元类的映射 cls->setSuperclass(supercls); cls->initClassIsa(metacls); if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro); // 设置实例大小 cls->setInstanceSize(ro->instanceSize); // 设置flag if (ro->flags & RO_HAS_CXX_STRUCTORS) { cls->setHasCxxDtor(); if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) { cls->setHasCxxCtor(); } } // 处理是否有关联对象 if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) || (supercls && supercls->forbidsAssociatedObjects())) { rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS; } // 关联类和父类,确认继承关系 if (supercls) { addSubclass(supercls, cls); } else { addRootClass(cls); } //Attach分类 methodizeClass(cls, previously); return cls; }
给class以及父类和元类对象的ro赋值以及给rw等成员赋值,最后调用methodizeClass;methodizeClass给方法排序,如果存在rwe,给rwe赋值,内部接着调用attachToClass函数:
static void methodizeClass(Class cls, Class previously) { // 修复方法列表、属性列表和协议列表 method_list_t *list = ro->baseMethods(); if (list) { // 对方法进行排序 prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr); if (rwe) rwe->methods.attachLists(&list, 1); } property_list_t *proplist = ro->baseProperties; if (rwe && proplist) { // attachLists主要作用是该方法会将新List按照原有顺序添加到旧的List前面并且扩容 rwe->properties.attachLists(&proplist, 1); } protocol_list_t *protolist = ro->baseProtocols; if (rwe && protolist) { rwe->protocols.attachLists(&protolist, 1); } // 附加分类 if (previously) { ... } objc::unattachedCategories.attachToClass(cls, cls, isMeta ? ATTACH_METACLASS : ATTACH_CLASS); }
attachToClass内部判断是否可以调用attachCategories函数,关于分类相关的加载流程、下篇文章具体分析:
void attachToClass(Class cls, Class previously, int flags) { //attachCategories: 当且仅当主类不实现load,所有的分类均实现load的时候才会被调用 if (it != map.end()) { 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); } }
建议此文结合源码通过断点调试,以及文中注释一起查看、更容易理解;
总结
调用流程大致如下:map_images
-> map_images_nolock
-> _read_images
-> 调用readClass以及realizeClassWithoutSwift等
-> methodizeClass
-> attachToClass
-> attachCategories
;主要几个步骤说明如下:
- readClass:把类和类名都加入到对应的表中
- realizeClassWithoutSwift:给class以及父类和元类对象的ro赋值给rw等成员赋值,经过此步后class中的ro以及rw都已经有值了,最后调用methodizeClass
- methodizeClass:给方法排序,如果存在rwe,给rwe赋值,内部接着调用attachToClass函数
- attachToClass:内部判断是否可以调用attachCategories函数
- attachCategories:如果来到这里,内部通过attachLists函数将分类方法列表、属性列表、协议列表合并到原来的方法列表中
- attachLists:主要是将新的list插入到旧的list的前面(内存平移以及内存拷贝的方式),此步函数具体实现下文分析;
本文部分内容来可能来源于网络,发布的内容如果侵犯了您的权益,请联系我们尽快删除!