OC地城探索(十七):C++构造方法 与 load 方法

1,359 阅读2分钟

为什么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);
  • doInitializationdoModInitFunctions, 当所有依赖的动态库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之前调用
  • loadmian 之间还有一种方法就是C++的构造方法
__attribute__((constructor))
static void look() {
    NSLog(@"main--%s", __func__);
}

image.png image.png

  • 所有的load 方法在 C++构造方法之前,都在main之前
  • 类别重写load不会覆盖,原类的load方法
  • load方法的调用顺序跟文件的排列有关
  • C++构造方法重名不会被覆盖,其调用顺序只有文件排列,加载顺序有关, 在文件内部,由上至下调用