OC底层 - 应用程序加载(3)
前言
一、recursiveInitialization
二、doInitialization
三、libSystem
四、libdispatch
五、load方法和c++方法执行
六、main函数的执行
七、load方法和initialize方法加载顺序分析
通过前面对dyld的分析,我们已经分析到了最核心的地方:递归初始化镜像文件。这篇文章就具体来分析初始化流程,比如初始化libSystem、libDispath、libObjc等系统库初始化的流程。
一、recursiveInitialization
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
recursive_lock lock_info(this_thread);
recursiveSpinLock(lock_info);
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) ) {
uninitUps.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);
// 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);
if ( hasInitializers ) {
uint64_t t2 = mach_absolute_time();
timingInfo.addTime(this->getShortName(), t2-t1);
}
}
catch (const char* msg) {
// this image is not initialized
fState = oldState;
recursiveSpinUnLock();
throw;
}
}
recursiveSpinUnLock();
}
首先整个递归流程加了一把递归锁。由于镜像文件也会依赖其他的库,所以这里会先递归初始化所有的依赖库。然后再初始化自身的库。
其中核心的初始化方法就是:doInitialization。
然后通过 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);
}
}
// mach message csdlc about dynamically unloaded images
if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) {
notifyKernel(*image, false);
const struct mach_header* loadAddress[] = { image->machHeader() };
const char* loadPath[] = { image->getPath() };
notifyMonitoringDyld(true, 1, loadAddress, loadPath);
}
}
可以看到(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
但是sNotifyObjCInit并没有找到。
我们全局搜索一下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;
// call 'mapped' function with all images mapped so far
try {
notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
}
catch (const char* msg) {
// ignore request to abort during registration
}
// <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
}
}
}
发现这个注册函数,而sNotifyObjCinit就是在这里赋值的。
继续全局搜索registerObjCNotifiers
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);
}
可以看到_dyld_objc_notify_register,这个函数在objc源码中出现过:
objc源码:
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();
tls_init();
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);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
显然_dyld_objc_notify_register是在_objc_init中注册的,那么我们应该在dyld调用(*sNotifyObjCInit)(image->getRealPath(), image->machHeader()) 之前就应该先初始化_objc_init才对。
所以接下来,我们回过头来具体分析: recursiveInitialization方法中的doInitialization。
二、doInitialization
bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
CRSetCrashLogMessage2(this->getPath());
// mach-o has -init and static initializers
doImageInit(context);
doModInitFunctions(context);
CRSetCrashLogMessage2(NULL);
return (fHasDashInit || fHasInitializers);
}
我们先来看doImageInit函数:
void ImageLoaderMachO::doImageInit(const LinkContext& context)
{
if ( fHasDashInit ) {
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) {
switch (cmd->cmd) {
case LC_ROUTINES_COMMAND:
Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide);
#if __has_feature(ptrauth_calls)
func = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
#endif
// <rdar://problem/8543820&9228031> verify initializers are in image
if ( ! this->containsAddress(stripPointer((void*)func)) ) {
dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
}
//libSystem 必须先初始化
if ( ! dyld::gProcessInfo->libSystemInitialized ) {
// <rdar://problem/17973316> libSystem initializer must run first
dyld::throwf("-init function in image (%s) that does not link with libSystem.dylib\n", this->getPath());
}
if ( context.verboseInit )
dyld::log("dyld: calling -init function %p in %s\n", func, this->getPath());
{
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);
}
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
}
可以看到,dyld从mach-o中读取所有的load_command,然后遍历每一个load_command,读取到初始化地址,然后执行func。
其中必须先初始化libSystem,所以我们进入libSystem的源码。
三、libSystem
__attribute__((constructor))
static void
libSystem_initializer(int argc,
const char* argv[],
const char* envp[],
const char* apple[],
const struct ProgramVars* vars)
{
static const struct _libkernel_functions libkernel_funcs = {
.version = 4,
// V1 functions
#if !TARGET_OS_DRIVERKIT
.dlsym = dlsym,
#endif
.malloc = malloc,
.free = free,
.realloc = realloc,
._pthread_exit_if_canceled = _pthread_exit_if_canceled,
// V2 functions (removed)
// V3 functions
.pthread_clear_qos_tsd = _pthread_clear_qos_tsd,
// V4 functions
.pthread_current_stack_contains_np = pthread_current_stack_contains_np,
};
static const struct _libpthread_functions libpthread_funcs = {
.version = 2,
.exit = exit,
.malloc = malloc,
.free = free,
};
static const struct _libc_functions libc_funcs = {
.version = 1,
.atfork_prepare = libSystem_atfork_prepare,
.atfork_parent = libSystem_atfork_parent,
.atfork_child = libSystem_atfork_child,
#if defined(HAVE_SYSTEM_CORESERVICES)
.dirhelper = _dirhelper,
#endif
};
static const struct _malloc_functions malloc_funcs = {
.version = 1,
#if !TARGET_OS_DRIVERKIT
.dlopen = dlopen,
.dlsym = dlsym,
#endif
};
_libSystem_ktrace0(ARIADNE_LIFECYCLE_libsystem_init | DBG_FUNC_START);
__libkernel_init(&libkernel_funcs, envp, apple, vars);
_libSystem_ktrace_init_func(KERNEL);
__libplatform_init(NULL, envp, apple, vars);
_libSystem_ktrace_init_func(PLATFORM);
__pthread_init(&libpthread_funcs, envp, apple, vars);
_libSystem_ktrace_init_func(PTHREAD);
_libc_initializer(&libc_funcs, envp, apple, vars);
_libSystem_ktrace_init_func(LIBC);
// TODO: Move __malloc_init before __libc_init after breaking malloc's upward link to Libc
// Note that __malloc_init() will also initialize ASAN when it is present
__malloc_init(apple);
_libSystem_ktrace_init_func(MALLOC);
#if TARGET_OS_OSX
/* <rdar://problem/9664631> */
__keymgr_initializer();
_libSystem_ktrace_init_func(KEYMGR);
#endif
//并不是 _dyld start了就一定是加载了,dyld也需要加载
_dyld_initializer();
_libSystem_ktrace_init_func(DYLD);
libdispatch_init();
_libSystem_ktrace_init_func(LIBDISPATCH);
#if !TARGET_OS_DRIVERKIT
_libxpc_initializer();
_libSystem_ktrace_init_func(LIBXPC);
#if CURRENT_VARIANT_asan
setenv("DT_BYPASS_LEAKS_CHECK", "1", 1);
#endif
#endif // !TARGET_OS_DRIVERKIT
// must be initialized after dispatch
_libtrace_init();
_libSystem_ktrace_init_func(LIBTRACE);
#if !TARGET_OS_DRIVERKIT
#if defined(HAVE_SYSTEM_SECINIT)
_libsecinit_initializer();
_libSystem_ktrace_init_func(SECINIT);
#endif
#if defined(HAVE_SYSTEM_CONTAINERMANAGER)
_container_init(apple);
_libSystem_ktrace_init_func(CONTAINERMGR);
#endif
__libdarwin_init();
_libSystem_ktrace_init_func(DARWIN);
#endif // !TARGET_OS_DRIVERKIT
__stack_logging_early_finished(&malloc_funcs);
#if !TARGET_OS_IPHONE
/* <rdar://problem/22139800> - Preserve the old behavior of apple[] for
* programs that haven't linked against newer SDK.
*/
#define APPLE0_PREFIX "executable_path="
if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11){
if (strncmp(apple[0], APPLE0_PREFIX, strlen(APPLE0_PREFIX)) == 0){
apple[0] = apple[0] + strlen(APPLE0_PREFIX);
}
}
#endif
#if TARGET_OS_OSX && !defined(__i386__)
bool enable_system_version_compat = false;
bool enable_ios_version_compat = false;
char *system_version_compat_override = getenv("SYSTEM_VERSION_COMPAT");
if (system_version_compat_override != NULL) {
long override = strtol(system_version_compat_override, NULL, 0);
if (override == 1) {
enable_system_version_compat = true;
} else if (override == 2) {
enable_ios_version_compat = true;
}
} else if (dyld_get_active_platform() == PLATFORM_MACCATALYST) {
if (!dyld_program_sdk_at_least(dyld_platform_version_iOS_14_0)) {
enable_system_version_compat = true;
}
} else if (dyld_get_active_platform() == PLATFORM_IOS) {
enable_ios_version_compat = true;
} else if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_16)) {
enable_system_version_compat = true;
}
if (enable_system_version_compat || enable_ios_version_compat) {
struct _libkernel_late_init_config config = {
.version = 2,
.enable_system_version_compat = enable_system_version_compat,
.enable_ios_version_compat = enable_ios_version_compat,
};
__libkernel_init_late(&config);
}
#endif // TARGET_OS_OSX && !defined(__i386__)
_libSystem_ktrace0(ARIADNE_LIFECYCLE_libsystem_init | DBG_FUNC_END);
/* <rdar://problem/11588042>
* C99 standard has the following in section 7.5(3):
* "The value of errno is zero at program startup, but is never set
* to zero by any library function."
*/
errno = 0;
}
可以看到libSystem_initializer中对很多系统库进行了初始化,其中_dyld_initializer()、libdispatch_init() 。
通过前面文章的堆栈,我们可以预先知道 其实objc_init是在libdispatch中执行的,所以我们再打开libdispatch的源码。
四、libdispatch
DISPATCH_EXPORT DISPATCH_NOTHROW
void
libdispatch_init(void)
{
dispatch_assert(sizeof(struct dispatch_apply_s) <=
DISPATCH_CONTINUATION_SIZE);
if (_dispatch_getenv_bool("LIBDISPATCH_STRICT", false)) {
_dispatch_mode |= DISPATCH_MODE_STRICT;
}
#if DISPATCH_DEBUG || DISPATCH_PROFILE
#if DISPATCH_USE_KEVENT_WORKQUEUE
if (getenv("LIBDISPATCH_DISABLE_KEVENT_WQ")) {
_dispatch_kevent_workqueue_enabled = false;
}
#endif
#endif
#if HAVE_PTHREAD_WORKQUEUE_QOS
dispatch_qos_t qos = _dispatch_qos_from_qos_class(qos_class_main());
_dispatch_main_q.dq_priority = _dispatch_priority_make(qos, 0);
#if DISPATCH_DEBUG
if (!getenv("LIBDISPATCH_DISABLE_SET_QOS")) {
_dispatch_set_qos_class_enabled = 1;
}
#endif
#endif
#if DISPATCH_USE_THREAD_LOCAL_STORAGE
_dispatch_thread_key_create(&__dispatch_tsd_key, _libdispatch_tsd_cleanup);
#else
_dispatch_thread_key_create(&dispatch_priority_key, NULL);
_dispatch_thread_key_create(&dispatch_r2k_key, NULL);
_dispatch_thread_key_create(&dispatch_queue_key, _dispatch_queue_cleanup);
_dispatch_thread_key_create(&dispatch_frame_key, _dispatch_frame_cleanup);
_dispatch_thread_key_create(&dispatch_cache_key, _dispatch_cache_cleanup);
_dispatch_thread_key_create(&dispatch_context_key, _dispatch_context_cleanup);
_dispatch_thread_key_create(&dispatch_pthread_root_queue_observer_hooks_key,
NULL);
_dispatch_thread_key_create(&dispatch_basepri_key, NULL);
#if DISPATCH_INTROSPECTION
_dispatch_thread_key_create(&dispatch_introspection_key , NULL);
#elif DISPATCH_PERF_MON
_dispatch_thread_key_create(&dispatch_bcounter_key, NULL);
#endif
_dispatch_thread_key_create(&dispatch_wlh_key, _dispatch_wlh_cleanup);
_dispatch_thread_key_create(&dispatch_voucher_key, _voucher_thread_cleanup);
_dispatch_thread_key_create(&dispatch_deferred_items_key,
_dispatch_deferred_items_cleanup);
#endif
pthread_key_create(&_os_workgroup_key, _os_workgroup_tsd_cleanup);
#if DISPATCH_USE_RESOLVERS // rdar://problem/8541707
_dispatch_main_q.do_targetq = _dispatch_get_default_queue(true);
#endif
_dispatch_queue_set_current(&_dispatch_main_q);
_dispatch_queue_set_bound_thread(&_dispatch_main_q);
#if DISPATCH_USE_PTHREAD_ATFORK
(void)dispatch_assume_zero(pthread_atfork(dispatch_atfork_prepare,
dispatch_atfork_parent, dispatch_atfork_child));
#endif
_dispatch_hw_config_init();
_dispatch_time_init();
_dispatch_vtable_init();
_os_object_init();
_voucher_init();
_dispatch_introspection_init();
}
可以看到_os_object_init:
void
_os_object_init(void)
{
_objc_init();
Block_callbacks_RR callbacks = {
sizeof(Block_callbacks_RR),
(void (*)(const void *))&objc_retain,
(void (*)(const void *))&objc_release,
(void (*)(const void *))&_os_objc_destructInstance
};
_Block_use_RR2(&callbacks);
#if DISPATCH_COCOA_COMPAT
const char *v = getenv("OBJC_DEBUG_MISSING_POOLS");
if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
v = getenv("DISPATCH_DEBUG_MISSING_POOLS");
if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
v = getenv("LIBDISPATCH_DEBUG_MISSING_POOLS");
if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
#endif
}
可以看到_objc_init出现了。此时,整体的流程就完美的形成了一个闭环。
五、load方法和c++方法执行
我们知道了_dyld_objc_notify_register的来龙去脉之后,再来看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;
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)
call_load_methods();
}
其中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;
}
可以看到先执行类的load,再执行分类的load。
注意:
1、这里说的“先执行类的load,再执行分类的load”,一定是在同一个动态库或者可执行文件中。比如动态库A比动态库B先插入链接,动态库A的分类+(void)load,就会早于动态库B主类的+(void)load以及分类的+(void)load调用。
2、假如一个类(并且没有+(void)load方法),只有一个分类中有+(void)load。也就是“懒加载类+非懒加载分类”(这个后面会具体的解释什么是懒加载类)。那么这个分类会在编译的时候就加载了,同时会改变这个类的编译顺序,使得这个类可以提前加载。
3、由此可见,动态库中类的load方法,一定比主程序优先调用。
再具体看下call_class_loads的函数:/***********************************************************************
* call_class_loads
* Call all pending class +load methods.
* If new classes become loadable, +load is NOT called for them.
*
* Called only by call_load_methods().
**********************************************************************/
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;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
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_method)(cls, @selector(load));
}
// Destroy the detached list.
if (classes) free(classes);
}
遍历所有的class,进行函数式调用load函数。
那么我可以通过一个demo来验证(在主程序中):

编译顺序如下:

我在截图的文件中,都加入了+(void)load方法,看打印结果:

总结:首先会执行父类的+(void)load,然后执行子类的+(void)load,然后再根据分类的编译顺序去执行分类的+(void)load。
那么为什么会先执行类的+(void)load,再执行子类的+(void)load。
这个问题会在后面的“类的加载”博客中具体分析。
先简单的说明一下:因为如果要调用load方法,首先需要先初始化类(realizeClassWithoutSwift)。初始化类的过程中,肯定是要初始化类的整个继承链。所以在执行load方法之前,父类也被初始化了。而且父类会排在子类之前。
以下函数可以证明:
// This list always has superclasses first because of the way it is constructed
//T他的列表总是首先有超类,因为它的构造方式
static struct loadable_class *loadable_classes = nil;
/***********************************************************************
* prepare_load_methods 中会调用schedule_class_load
* Schedule +load for classes in this image, any un-+load-ed
* superclasses in other images, and any categories in this image.
**********************************************************************/
// Recursively schedule +load for cls and any un-+load-ed superclasses.
// cls must already be connected.
static void schedule_class_load(Class cls)
{
if (!cls) return;
ASSERT(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
schedule_class_load(cls->getSuperclass());
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
+(void)load 方法我们分析完了,那么c++方法是在什么时候去调用的呢?
我们再回到dyld的源码中:
刚才我们分析了doImageInit的流程,那么下面紧跟着就是doModInitFunctions这个函数。
这个函数就是去遍历执行c++方法的。
所以在我们最开始的问题中,就验证了为什么先执行 +(void)load,然后再执行c++。
六、main函数的执行
在dyld的main函数执行完之后,也就是所有的初始化和加载、链接等完成之后,会返回一个main()的函数地址。我们可以通过断点调试来证明:

我们走完dyld_start之后,我们读寄存器,可以看到rax = main的地址。最终跳转到main函数。
七、load方法和initialize方法加载顺序分析
load方法我们已经分析过了,是在main函数之前,所以它一定是在initialize之前,那么initialize我们在对消息的慢速查找流程中分析过:
/***************************************************** **********************
* realizeAndInitializeIfNeeded_locked
* 如果尚未实现,则实现给定的类,如果尚未初始化,则对其进行初始化。
* inst 是 cls 或子类的实例,如果不知道则为 nil。
* cls 是要初始化和实现的类。
* initializer 为 true 初始化类,false 跳过初始化。 ****************************************************** ************************/
cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
/*
if (slowpath(!cls->isRealized())) {
对整个继承链和元类 都进行ro->rw->rwe 进行赋值,同时也验证了class为一个双向链表结构 (做类数据的准备工作)
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
if (slowpath(initialize && !cls->isInitialized())) {
//如果类没有初始化, 会进行初始化,并执行callInitialize(cls); 也就是每一个类在第一次使用的时候会默认走+ (void)initialize;方法的原因.
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
// runtimeLock may have been dropped but is now locked again
// If sel == initialize, class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
*/
一个类在第一次使用的时候,会去针对整个继承链去做初始化数据准备,并执行callInitialize(cls);
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}
可以看到initialize走的是消息发送,那么也验证了+ (void)initialize 一定是在+ (void)load之后的。
并且,+ (void)initialize 和 +(void)load一样,会先执行父类的+ (void)initialize方法,再执行子类的+ (void)initialize方法。
另外这里有一个重点,就是如果子类没有实现+ (void)initialize,而父类实现了,那么父类里面的+ (void)initialize会走2次,因为当子类去查找initialize时不存在的时候,会递归查找父类的方法。
那么对于分类呢,就像我们在探索消息慢速查找流程一样,在二分查找方法中,如果找到方法后,会继续向前去查找是否有相同名称的分类方法。
所以,如果分类里面也实现了+ (void)initialize,就会覆盖原来类的方法。
如果有多个分类实现了+ (void)initialize,那么就执行加载的最后一个分类的方法。因为上面说了:
在二分查找方法中,如果找到方法后,会继续向前去查找是否有相同名称的分类方法
再后面探索分类的时候,会举例子来再次复习这个重要的知识点。
总结流程图:
