为什么load方法在main方法之前
- 之前探索了
dyld的链接流程,了解到了,在调用main函数之前,会先调用底层objc_init来初始化链接runtime - 下面是
objc_init的源码
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();
//线程key的绑定
tls_init();
//调用 libc 中 C++ 构造函数,在 dylb调用构造函数之前
static_init();
//运行时环境初始化
runtime_init();
// 异常处理
exception_init();
#if __OBJC2__
//缓存初始化
cache_t::init();
#endif
//执行回调
_imp_implementationWithBlock_init();
// 注册dyld回调
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
static_init调用C++的构造函数,但这个是在dyld之前,只会调用objc源码中构造函数- 查看一下
load_images源码,这个是作为回调函数,交给dyld调用的, 在map_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
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
// 调用所有 load 方法
call_load_methods();
}
dyld在调用load_images函数之后调用了doInitialization
// let objc know we are about to initialize this image
// 让对象知道我们将于奥初始化这个image, 这里是调用 loadImages
uint64_t t1 = mach_absolute_time();
fState = dyld_image_state_dependents_initialized;
oldState = fState;
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
// initialize this image
// 初始化image, 调用类里所有的C++构造函数
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);
doInitialization调doModInitFunctions, 当所有依赖的动态库libsystem都初始化化过了,就会调用image所有的构造函数
if ( context.verboseInit )
dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
{
// DBG_DYLD_TIMING_STATIC_INITIALIZER
dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
}
dyld -> objc_init -> static_init(runtime源码的构造方法) -> map_images -> load_images(load方法) -> doInitialization(dyld调用,所有类文件里的构造方法) -> main- 所以
load方法在main之前
C++构造方法和load方法的调用顺序
- 上面讨论了
load方法在main之前调用 - 在
load和mian之间还有一种方法就是C++的构造方法
__attribute__((constructor))
static void look() {
NSLog(@"main--%s", __func__);
}
- 所有的
load方法在C++构造方法之前,都在main之前 - 类别重写
load不会覆盖,原类的load方法 load方法的调用顺序跟文件的排列有关C++构造方法重名不会被覆盖,其调用顺序只有文件排列,加载顺序有关, 在文件内部,由上至下调用