前言
在之前的文章中,我们介绍了对象和类的数据,属性、方法、成员变量等。这些都是通过代码实现,都需要加载到内存中我们才能使用,要不然只是文件,今天我们来探索它们是如何加载到应用程序的。
准备工作
一、应用程序的加载原理
每个应用程序加载都需要一些底层的库,UIKit、CoreFoundation、AVFoundation等等。库是可执行的二进制文件,能被操作系统加载到内存,有静态库和动态库。
编译过程
可执行文件
1. 项目工程 可执行文件
创建一个macOS的工程:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
}
return 0;
}
- 代码就是默认的打印,不做修改。
接下来生成可执行文件,并拖到terminal中:
- 如上图,可执行文件拖到
终端中是可以执行操作的,打印出了Hello,World!。
2. 系统库 可执行文件
查找系统Foundation可执行文件:
- 通过
image list获取到Foundation可执行文件路径,最终在磁盘中成功找到。
静态链接 和 动态链接
- 动态链接方式可以共享动态库,优化了内存空间,所以苹果的库都是动态库。
加载过程
库是通过dyld(动态连接器)加载到内存中来的,整体流程可以用下面的图来表示:
二、dyld 的引出
我们先创建一个iOS的工程,在ViewController.m中添加load方法:
@implementation ViewController
+ (void)load{
NSLog(@"%s",__func__);
}
@end
在main函数处打一个断点,运行程序:
- 程序成功断在了
main函数,我们发现在main函数之前还调用了一个start函数,那么先添加一个start的符号断点进行调试。
添加start符号断点,再次运行程序:
- 添加的
start符号断点并没有断住,程序还是走到了main函数,说明这些符号断点并不是start的实现。在main函数前+[ViewController load]被调用了,那么就在load方法打一个断点进行调试。
在ViewController的load方法打断点,运行程序:
- 程序断在
load方法后,通过bt打印堆栈,在堆栈中发现了_dyld_start函数。到这里也是引出了dyld,点击 dyld-852 进行源码的下载,接下来进行dyld源码的探索。
三、dyld 流程上
源码中全局搜索_dyld_start:
- 我们在
dyldStartup.s文件中找到了_dyld_start的实现。并且看到了call dyldbootstrap::start这样的代码,dyldbootstrap是C++中的命名空间,说明start的实现在这个命名空间下。
在dyldbootstrap命名空间中找到start函数:
uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* argv[],
const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue)
{
...
return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}
- 可以看到
start函数中,返回了_main函数,接下来对_main进行分析。
四、dyld 流程中的 main 函数主流程
点击进入_main函数:
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
int argc, const char* argv[], const char* envp[], const char* apple[],
uintptr_t* startGlue)
{
...
// 整个主程序的一些信息的处理,CPU、架构等
getHostInfo(mainExecutableMH, mainExecutableSlide);
// 镜像文件的一些处理
{
__block bool platformFound = false;
((dyld3::MachOFile*)mainExecutableMH)->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
if (platformFound) {
halt("MH_EXECUTE binaries may only specify one platform");
}
gProcessInfo->platform = (uint32_t)platform;
platformFound = true;
});
}
// 路径相关的一些操作
const char* rootPath = _simple_getenv(envp, "DYLD_ROOT_PATH");
if ( (rootPath != NULL) ) {
...
}
else {
...
}
// 共享缓存加载 load shared cache
mapSharedCache(mainExecutableSlide);
// 实例化主程序 instantiate ImageLoader for main executable
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
// 加载插入的动态库 load any inserted libraries
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
// link 主程序
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
// 循环递归绑定 Bind and notify for the inserted images now interposing has been registered
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
image->recursiveBind(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true, nullptr);
}
}
// 弱引用绑定主程序 <rdar://problem/12186933> do weak binding only after all inserted images linked
sMainExecutable->weakBind(gLinkContext);
gLinkContext.linkingMainExecutable = false;
// 初始化 run all initializers
initializeMainExecutable();
// 通知dyld可以进main()函数了 notify any montoring proccesses that this process is about to enter main()
notifyMonitoringDyldMain();
result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
return result;
}
五、initializeMainExecutable 流程-主程序运行
进入initializeMainExecutable函数:
void initializeMainExecutable()
{
// run initialzers for any inserted dylibs
// allImagesCount():拿到所有镜像文件的个数
ImageLoader::InitializerTimingList initializerTimes[allImagesCount()];
initializerTimes[0].count = 0;
const size_t rootCount = sImageRoots.size();
if ( rootCount > 1 ) {
for(size_t i=1; i < rootCount; ++i) {
// 镜像文件初始化
sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]);
}
}
// 主程序初始化 run initializers for main executable and everything it brings up
sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
}
- 可以发现不管是镜像文件初始化,还是主程序的初始化,都是调用了
runInitializers。
runInitializers
进入runInitializers函数:
- 这个函数的重点是
processInitializers函数。
processInitializers
进入processInitializers函数:
void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread,
InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images)
{
uint32_t maxImageCount = context.imageCount()+2;
ImageLoader::UninitedUpwards upsBuffer[maxImageCount];
ImageLoader::UninitedUpwards& ups = upsBuffer[0];
ups.count = 0;
// Calling recursive init on all images in images list, building a new list of
// uninitialized upward dependencies.
for (uintptr_t i=0; i < images.count; ++i) {
images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups);
}
// If any upward dependencies remain, init them.
if ( ups.count > 0 )
processInitializers(context, thisThread, timingInfo, ups);
}
- 这个函数的重点是
recursiveInitialization函数。
recursiveInitialization
进入recursiveInitialization函数:
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
if ( fState < dyld_image_state_dependents_initialized-1 ) {
uint8_t oldState = fState;
// break cycles
fState = dyld_image_state_dependents_initialized-1;
try {
// initialize lower level libraries first
// 依赖文件递归初始化
for(unsigned int i=0; i < libraryCount(); ++i) {
ImageLoader* dependentImage = libImage(i);
if ( dependentImage != NULL ) {
// don't try to initialize stuff "above" me yet
if ( libIsUpward(i) ) {
tUps.imagesAndPaths[uninitUps.count] = { dependentImage, libPath(i) };
uninitUps.count++;
}
else if ( dependentImage->fDepth >= fDepth ) {
dependentImage->recursiveInitialization(context, this_thread, libPath(i), timingInfo, uninitUps);
}
}
}
// record termination order
if ( this->needsTermination() )
context.terminationRecorder(this);
// let objc know we are about to initialize this image
uint64_t t1 = mach_absolute_time();
fState = dyld_image_state_dependents_initialized;
oldState = fState;
// 单个通知注入
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
// 调用init方法 initialize this image
bool hasInitializers = this->doInitialization(context);
// let anyone know we finished initializing this image
fState = dyld_image_state_initialized;
oldState = fState;
// 通知初始化完成
context.notifySingle(dyld_image_state_initialized, this, NULL);
}
catch (const char* msg) {
...
}
}
}
context.notifySingle:单个通知注入。this->doInitialization:调用init方法。context.notifySingle:通知初始化完成。
notifySingle
全局搜索,找到了notifySingle函数的赋值:
点进进入notifySingle:
static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
//dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath());
std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(state, sSingleHandlers);
if ( handlers != NULL ) {
dyld_image_info info;
info.imageLoadAddress = image->machHeader();
info.imageFilePath = image->getRealPath();
info.imageFileModDate = image->lastModified();
for (std::vector<dyld_image_state_change_handler>::iterator it = handlers->begin(); it != handlers->end(); ++it) {
const char* result = (*it)(state, 1, &info);
if ( (result != NULL) && (state == dyld_image_state_mapped) ) {
//fprintf(stderr, " image rejected by handler=%p\n", *it);
// make copy of thrown string so that later catch clauses can free it
const char* str = strdup(result);
throw str;
}
}
}
if ( state == dyld_image_state_mapped ) {
// <rdar://problem/7008875> Save load addr + UUID for images from outside the shared cache
// <rdar://problem/50432671> Include UUIDs for shared cache dylibs in all image info when using private mapped shared caches
if (!image->inSharedCache()
|| (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion)) {
dyld_uuid_info info;
if ( image->getUUID(info.imageUUID) ) {
info.imageLoadAddress = image->machHeader();
addNonSharedCacheImageUUID(info);
}
}
}
if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
uint64_t t0 = mach_absolute_time();
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
uint64_t t1 = mach_absolute_time();
uint64_t t2 = mach_absolute_time();
uint64_t timeInObjC = t1-t0;
uint64_t emptyTime = (t2-t1)*100;
if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) {
timingInfo->addTime(image->getShortName(), timeInObjC);
}
}
}
- 定位关键代码
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());。
sNotifyObjCInit
搜索查找,得到sNotifyObjCInit的相关代码:
static _dyld_objc_notify_init sNotifyObjCInit;
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
}
sNotifyObjCInit是_dyld_objc_notify_init类型的,在registerObjCNotifiers函数中被赋的值,那么registerObjCNotifiers又是在哪儿里被调用的呢。
registerObjCNotifiers
全局搜索registerObjCNotifiers函数:
- 在
_dyld_objc_notify_register函数中找到了registerObjCNotifiers的调用,而_dyld_objc_notify_register我们也是见过的。
看下 objc4-818.2 源码 中 _objc_init函数的实现:
- 在这里找到了
_dyld_objc_notify_register的调用,那么下面我们以_objc_init为切入点继续探索。
六、images 初始化流程
我们接下来,将以_objc_init为切入点,反向推导流程。
打开 objc4-818.2 源码 ,在_objc_init处打断点,运行程序:
- 可以看到,在
_objc_init函数之前调用了_os_object_init,这个函数在libdispatch库中。
_os_object_init
下载 libdispatch 源码 ,在源码中全局搜索_os_object_init函数:
- 找到了
_os_object_init函数的实现,并在其中找到了_objc_init()函数的调用,目前得到流程:_os_object_init->_objc_init()。
再看下_os_object_init函数之前调用了什么函数:
- 可以看到是
libdispatch_init被调用了,它也属于libdispatch库。
libdispatch_init
全局搜索,得到libdispatch_init函数:
libdispatch_init(void)
{
...
_dispatch_hw_config_init();
_dispatch_time_init();
_dispatch_vtable_init();
_os_object_init();
_voucher_init();
_dispatch_introspection_init();
}
- 在
libdispatch_init的实现中,找到了_os_object_init()的调用,目前得到流程:libdispatch_init->_os_object_init->_objc_init()。
再次分析堆栈,查看libdispatch_init之前的函数调用:
libdispatch_init之前调用了libSystem_initializer函数,libSystem_initializer属于libSystem库,接下来继续验证。
libSystem_initializer
下载 Libsystem 源码 ,在源码中全局搜索libSystem_initializer函数:
- 找到了
libSystem_initializer函数的实现,并在239行找到了libdispatch_init函数的调用,目前得到流程:libSystem_initializer->libdispatch_init->_os_object_init->_objc_init()。
查看libSystem_initializer上一步函数的调用:
- 可以看到上一步的调用是
doModInitFunctions函数,这次又回到了dyld。
doModInitFunctions
找到doModInitFunctions,进入函数:
void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)
{
if ( fHasInitializers ) {
const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
const struct load_command* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
// macho 中相关 command 的加载
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
const uint8_t type = sect->flags & SECTION_TYPE;
if ( type == S_MOD_INIT_FUNC_POINTERS ) {
Initializer* inits = (Initializer*)(sect->addr + fSlide);
for (size_t j=0; j < count; ++j) {
// 获取各 Initializer 包括 libSystem_initializer
Initializer func = inits[j];
if ( ! dyld::gProcessInfo->libSystemInitialized ) {
// <rdar://problem/17973316> libSystem initializer must run first
// libSystem 必须最先加载,否则就会报错
const char* installPath = getInstallPath();
if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) )
dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath());
}
bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
{
dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
// Initializer 包括 libSystem_initializer 的调用
func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
}
if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
// now safe to use malloc() and other calls in libSystem.dylib
dyld::gProcessInfo->libSystemInitialized = true;
}
}
}
else if ( type == S_INIT_FUNC_OFFSETS ) {
// 常规的映射等
...
}
}
}
}
}
}
- 通过函数分析,可以知道
libSystem是第一个会被加载的镜像文件,否则会报错。 UIKit、Foundation等其他的库,都会依赖Runtime的基础、线程的基础、环境基础等,所以要最先加载libSystem。Initializer func = inits[j];第一次获取的func就是libSystem_initializer,并通过func(context.argc, context.argv, context.envp, context.apple, &context.programVars);进行调用,接下来查看doModInitFunctions的调用。- 目前得到流程:
doModInitFunctions->libSystem_initializer->libdispatch_init->_os_object_init->_objc_init()。
搜索doModInitFunctions,查看它的调用:
OK!!,回到了我们上面提到的函数doInitialization。- 目前得到流程:
doInitialization->doModInitFunctions->libSystem_initializer->libdispatch_init->_os_object_init->_objc_init()。
七、dyld 链接 objc 的函数执行
通过之前的分析,我们得到doInitialization后面的流程如下:
doInitialization -> doModInitFunctions -> libSystem_initializer -> libdispatch_init -> _os_object_init -> _objc_init() -> _dyld_objc_notify_register -> registerObjCNotifiers。
再回顾下_dyld_objc_notify_register和registerObjCNotifiers函数的调用,以及函数实现的关键代码:
void _objc_init(void)
{
...
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
...
}
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped)
{
dyld::registerObjCNotifiers(mapped, init, unmapped);
}
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
...
}
- 通过对比分析可以得出,
map_images()=sNotifyObjCMapped(),load_images()=sNotifyObjCInit()。 - 接下来探索一下
map_images()和load_images()在哪里被调用了。
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
全局搜索sNotifyObjCMapped:
- 在
notifyBatchPartial函数中,找到了sNotifyObjCMapped的调用。
全局搜索notifyBatchPartial:
- 在
registerObjCNotifiers函数中找到了notifyBatchPartial的调用,原来sNotifyObjCMapped函数在这个函数中被赋值完,就直接被调用了。
sNotifyObjCInit在哪儿里被调用了呢,继续搜索:
- 在
notifySingle函数中,找到sNotifyObjCInit的调用。
notifySingle的调用上面已经聊到过,它和doInitialization一样是在recursiveInitialization中被调用的:
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
if ( fState < dyld_image_state_dependents_initialized-1 ) {
try {
// 单个通知注入
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
// 调用init方法 initialize this image
bool hasInitializers = this->doInitialization(context);
// let anyone know we finished initializing this image
fState = dyld_image_state_initialized;
oldState = fState;
// 通知初始化完成
context.notifySingle(dyld_image_state_initialized, this, NULL);
}
catch (const char* msg) {
...
}
}
}
- 看代码
context.notifySingle是在this->doInitialization前面就被调用了,而sNotifyObjCInit是在doInitialization才被注册,这是为什么呢? - 因为
recursiveInitialization是个递归函数,第一次调用notifySingle时sNotifyObjCInit没有被初始化,第二次进来时sNotifyObjCInit就有值了。 - 总结一下,第一次进入时会先调用
doInitialization,函数内对map_images和load_images进行初始化,紧接这调用map_images。然后走到下面的notifySingle函数,会调用load_images函数。
八、load 方法调用
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;
// Discover load methods
{
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
prepare_load_methods函数,会做一些准备工作,将主类的load方法和分类的load方法分别加到各自的表中。call_load_methods函数,会对主类load方法和分类的load方法进行调用。
prepare_load_methods
进入prepare_load_methods:
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
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);
}
}
- 主类会循环调用
schedule_class_load函数,来添加load方法。 - 分类会循环调用
add_category_to_loadable_list函数,来添加load方法。
1. 主类 load方法 添加
进入schedule_class_load:
static void schedule_class_load(Class cls)
{
// 如果为 nil 函数返回
if (!cls) return;
// 如果flags为RW_LOADED 说明已经load过,函数返回
if (cls->data()->flags & RW_LOADED) return;
// 父类递归调用本函数
schedule_class_load(cls->getSuperclass());
// 添加到表里
add_class_to_loadable_list(cls);
// 设置flags为RW_LOADED 标识已经load过
cls->setInfo(RW_LOADED);
}
进入add_class_to_loadable_list:
void add_class_to_loadable_list(Class cls)
{
// 获取load 方法的imp
IMP 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());
}
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
// 将cls 和method 赋值到loadable_classes 表中
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
loadable_classes是数组,数组中的元素是loadable_class,loadable_class的数据结构:cls和method通过loadable_class结构进行赋值,每增加一个load就会loadable_classes_used++,用来统计load的数量和方便下次的赋值。
2. 分类 load方法 添加
void add_category_to_loadable_list(Category cat)
{
IMP method = _category_getLoadMethod(cat);
// Don't bother if cat has no +load method
if (!method) return;
if (PrintLoading) {
_objc_inform("LOAD: category '%s(%s)' scheduled for +load",
_category_getClassName(cat), _category_getName(cat));
}
if (loadable_categories_used == loadable_categories_allocated) {
loadable_categories_allocated = loadable_categories_allocated*2 + 16;
loadable_categories = (struct loadable_category *)
realloc(loadable_categories,
loadable_categories_allocated *
sizeof(struct loadable_category));
}
// 将cls 和method 赋值到loadable_categories 表中
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
分类的load方法添加,和主类的添加方式是一样的,只不过是不同的表而已,分类的数组元素是loadable_category,数据结构表示为:
call_load_methods
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;
}
do while循环调用call_class_loads方法,来调用类的load方法。do while循环调用call_category_loads方法,来调用分类的load方法。
1. call_class_loads 调用主类load方法
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;
// for 循环遍历主类load表
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
// 将load 方法的imp,从主类load表中取出来
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
// 直接调用load方法的imp,传参cls和@selector(load)
(*load_method)(cls, @selector(load));
}
// Destroy the detached list.
if (classes) free(classes);
}
注意:这里
load方法的调用,是直接imp的调用,不是发送消息。
2. call_category_loads 调用分类load方法
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;
// for 循环遍历分类load表
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
// 将load 方法的imp,从分类load表中取出来
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方法的imp,传参cls和@selector(load)
(*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方法的调用,也是直接imp的调用,不是发送消息。
九、dyld 流程分析图
今天是对dyld的大体流程做了分析,下一篇将对类加载等详细信息进行探索,点个赞支持一下吧!!😄😄😄。