前面我们介绍了Mach O的结构,App的启动,dyld的加载,以及通过dyld将镜像加载进来,经过rebase/Bind处理后,找到main入口,以及Runtime相关的数据结构,有了前面的一系列铺垫这里介绍Runtime初始化就显得相对轻松了,这篇博客我们从《iOS Runtime源码解析之dyld》结尾处接着介绍,《iOS Runtime源码解析之dyld》中介绍了从dyld入口__dyld_start作为起点,到找到并跳转到主函数入口期间的一系列工作:

1. 将主程序初始化为imageLoader
2. 加载共享库到内存
3. 加载插入的动态库
4. 链接主程序,链接插入库
5. 初始化主程序,插入库
6. 寻找主程序入口点

这篇博客和《iOS Runtime源码解析之dyld》的衔接点就在于第5步初始化主程序,插入库这一步,在这个阶段,会调用使用attribute((constructor) 进行修饰的方法,其中有个十分重要的动态库初始化方法,libSystem_initializer

static __attribute__((constructor)) 
void libSystem_initializer(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars) {

//......

_libkernel_init(libkernel_funcs);

bootstrap_init();
mach_init();
pthread_init();
__libc_init(vars, libSystem_atfork_prepare, libSystem_atfork_parent, libSystem_atfork_child, apple);
__keymgr_initializer();
_dyld_initializer();
libdispatch_init();
_libxpc_initializer();

__stack_logging_early_finished();
//.......
errno = 0;
}

在libSystem.dylib的初始化方法libSystem_initializer中初始化了多了dylib库,比如:liblaunch.dylib,libc.a,libdispatch.a等等,这里最关键的是****libdispatch_init****:

void libdispatch_init(void) {
//......
_os_object_init();
}

在libdispatch_init的最后部分会调用****_os_object_init****:

void
_os_object_init(void)
{
return _objc_init();
}

_os_object_init方法只有一行代码就是转调_objc_init,这就是我们十分关注的Runtime 初始化的入口。


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();
lock_init();
exception_init();

//_dyld_objc_notify_register(&map_2_images, load_images, unmap_image);
//向dyld注册了回调函数,当imagemap到内存中,当初始化完成image时和卸载image的时候都会回调注册者
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

_objc_init向dyld注册了map_images,load_images,unmap_image三个关键的回调函数,各个关键阶段节点常量定义如下:

enum dyld_image_states
{
dyld_image_state_mapped = 10, // No batch notification for this
dyld_image_state_dependents_mapped = 20, // Only batch notification for this
dyld_image_state_rebased = 30,
dyld_image_state_bound = 40,
dyld_image_state_dependents_initialized = 45, // Only single notification for this
dyld_image_state_initialized = 50,
dyld_image_state_terminated = 60 // Only single notification for this
};

我们看下****_dyld_objc_notify_register****这个方法的注释,通过注释我们可以了解到,在runtime中可以通过这个方法可以向dyld注册用于处理镜像完成映射,取消映射和初始化之后的处理方法。

//
// Note: only for use by objc runtime
// Register handlers to be called when objc images are mapped, unmapped, and initialized.
// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
// call dlopen() on them to keep them from being unloaded. During the call to _dyld_objc_notify_register(),
// dyld will call the "mapped" function with already loaded objc images. During any later dlopen() call,
// dyld will also call the "mapped" function. Dyld will call the "init" function when dyld would be called
// initializers in that image. This is when objc calls any +load methods in that image.
// 通过这个方法可以向dyld注册用于处理镜像完成映射,取消映射和初始化之后的处理方法。

void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped);

我们重点关注下****_dyld_objc_notify_mapped以及_dyld_objc_notify_init****,

  • _dyld_objc_notify_mapped

map_images会在主程序以及其他库加载进来后调用ImageLoader::setMapped发出通知,调用map_images:

void ImageLoader::setMapped(const LinkContext& context)
{
fState = dyld_image_state_mapped;
context.notifySingle(dyld_image_state_mapped, this); // note: can throw exception
}

ImageLoader::setMapped会在instantiateMainExecutable实例话主程序,instantiateFromFile实例化镜像的时候被调用:

  • _dyld_objc_notify_init

会在ImageLoader::runInitializers 方法中被调用,而ImageLoader::runInitializers 则会在 initializeMainExecutable中调用。

也即是说在实例化主程序或者其他dylib库的时候都会发出_dyld_objc_notify_mapped通知,这时候会调用runtime 的 map_images方法进行后续处理,在这些初始化完成的时候会发出_dyld_objc_notify_init通知调用load_images方法进行后续处理。

我们紧接着来看下map_images以及load_images的处理,其实在介绍分类的时候已经介绍过了map_images这里为保证整个文章的完整性再过一遍这部分内容:

  • 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);
}

void 
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
//......
if (hCount > 0) {
//读取镜像信息
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
}

void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
//......

// =================================查找classes=================================
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
for (EACH_HEADER) {
//从Mach-O的 __DATA区 __objc_classlist 获取所有类,并加入gdb_objc_realized_classes list中
classref_t *classlist = _getObjc2ClassList(hi, &count);
//.......
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);

if (newCls != cls && newCls) {
// 类被移动了但是没有被删除的情况
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)realloc(resolvedFutureClasses, (resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}

ts.log("IMAGE TIMES: discover classes");

// 修复重新映射的类
// Fix up remapped classes
// Class list and nonlazy class list remain unremapped.
// Class refs and super refs are remapped for message dispatching.

if (!noClassesRemapped()) {
for (EACH_HEADER) {
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
// fixme why doesn't test future1 catch the absence of this?
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}

// 重新映射类
ts.log("IMAGE TIMES: remap classes");

// Fix up @selector references
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->isPreoptimized()) continue;

bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
// 3. 注册Sel,并存储到全局变量namedSelectors的list中
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
}

ts.log("IMAGE TIMES: fix up selector references");

//.....

// Discover protocols. Fix up protocol refs.
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
assert(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->isPreoptimized();
bool isBundle = hi->isBundle();
//找到所有Protocol并处理引用
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}

ts.log("IMAGE TIMES: discover protocols");

// Fix up @protocol references
// Preoptimized images may have the right
// answer already but we don't know for sure.
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}

ts.log("IMAGE TIMES: fix up @protocol references");

// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
addClassTableEntry(cls);
realizeClass(cls);
}
}

ts.log("IMAGE TIMES: realize non-lazy classes");

// Realize newly-resolved future classes, in case CF manipulates them
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
realizeClass(resolvedFutureClasses[i]);
resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}

ts.log("IMAGE TIMES: realize future classes");

// Discover categories.
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
//查看是否包含属性
bool hasClassProperties = hi->info()->hasCategoryClassProperties();

for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
//获取到所属的类
Class cls = remapClass(cat->cls);

if (!cls) {
// Category's target class is missing (probably weak-linked).
// Disavow any knowledge of this category.
catlist[i] = nil;
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}

// Process this category.
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
bool classExists = NO;
//如果有实例方法,协议或者实例属性
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
//将分类添到属性,方法,协议添加到对应的类中
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
}
//如果有类方法,协议或者类属性
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
}
}

ts.log("IMAGE TIMES: discover categories");

// Category discovery MUST BE LAST to avoid potential races
// when other threads call the new category code before
// this thread finishes its fixups.

// +load handled by prepare_load_methods()

if (DebugNonFragileIvars) {
realizeAllClasses();
}
//......
}

map_images 最关键的代码在_read_images方法中,****_read_images故名思议就是读取镜像,在这个方法中会从镜像的_DATA区域通过_getObjc2XXXX****将该镜像的类列表,分类列表,协议列表读取出来,对应的方法以及读取的session部分可以查看如下声明,这里需要提一下_DATA区域中的这些session数据是由编译器写入的。

//      function name                 content type     section name
GETSECT(_getObjc2SelectorRefs, SEL, "__objc_selrefs");
GETSECT(_getObjc2MessageRefs, message_ref_t, "__objc_msgrefs");
GETSECT(_getObjc2ClassRefs, Class, "__objc_classrefs");
GETSECT(_getObjc2SuperRefs, Class, "__objc_superrefs");
GETSECT(_getObjc2ClassList, classref_t, "__objc_classlist");
GETSECT(_getObjc2NonlazyClassList, classref_t, "__objc_nlclslist");
GETSECT(_getObjc2CategoryList, category_t *, "__objc_catlist");
GETSECT(_getObjc2NonlazyCategoryList, category_t *, "__objc_nlcatlist");
GETSECT(_getObjc2ProtocolList, protocol_t *, "__objc_protolist");
GETSECT(_getObjc2ProtocolRefs, protocol_t *, "__objc_protorefs");
GETSECT(getLibobjcInitializers, UnsignedInitializer, "__objc_init_func");

这个方法的每个阶段结束都会调用ts.log(“IMAGE TIMES:XXXXXX)来提示每个阶段结束,在这些阶段中有我们之前分析过的Catogies 加载,以及协议加载。我们重点看下最末尾的realizeAllClasses方法:

static void realizeAllClasses(void)
{
runtimeLock.assertLocked();
header_info *hi;
for (hi = FirstHeader; hi; hi = hi->getNext()) {
realizeAllClassesInImage(hi);
}
}
static void realizeAllClassesInImage(header_info *hi) {

runtimeLock.assertLocked();

size_t count, i;
classref_t *classlist;

if (hi->areAllClassesRealized()) return;

classlist = _getObjc2ClassList(hi, &count);

for (i = 0; i < count; i++) {
realizeClass(remapClass(classlist[i]));
}

hi->setAllClassesRealized(YES);
}

realizeAllClasses会对镜像中的所有class调用realizeClass方法。

static Class realizeClass(Class cls) {

//.....
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;

if (!cls) return nil; //cls 不能为空
if (cls->isRealized()) return cls; //cls 如果已经初始化直接返回
assert(cls == remapClass(cls)); //cls 没有重新分配,remapClass 返回指向cls的实时指针
// 【✨】获取只读结构体
ro = (const class_ro_t *)cls->data();

// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1); //分配读写数据
rw->ro = ro; //只写数据
rw->flags = RW_REALIZED|RW_REALIZING; //设置已经初始化标志
cls->setData(rw); //为cls设置data数据

isMeta = ro->flags & RO_META; //判断是否是元类
rw->version = isMeta ? 7 : 0; // old runtime went up to 6 //版本信息,旧版本的版本信息为6

//.....
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
// 【✨】为supercls,metacls 分配空间
supercls = realizeClass(remapClass(cls->superclass));
metacls = realizeClass(remapClass(cls->ISA()));

//......

// 【✨】 Update superclass and metaclass in case of remapping
cls->superclass = supercls; //将supercls赋给 cls->superclass
//将上面分配的metacls赋给cls
cls->initClassIsa(metacls);

// 调整实例变量的偏移和布局,这个将会重新分配class_ro_t
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);

//设置对象尺寸
// Set fastInstanceSize if it wasn't set already.
cls->setInstanceSize(ro->instanceSize);

//【✨】从ro中拷贝部分标志位到rw 字段
// Copy some flags from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}

// 【✨】 Connect this class to its superclass's subclass lists
// 将当前class与父类相关连
if (supercls) {
//将当前类作为supercls的子类添加到父类的子类列表
addSubclass(supercls, cls);
} else {
//将当前类作为根类
addRootClass(cls);
}

//【✨】实例化类结构
// Attach categories
// 使得类有条理
methodizeClass(cls);

return cls;
}

realizeClass 方法实际上是为类的class_rw_t,superclass,metacls等分配空间,并初始化。在最后的时候会调用methodizeClass进行进一步的初始化:

static void methodizeClass(Class cls)
{
//检查锁
runtimeLock.assertLocked();
//是否是元类
bool isMeta = cls->isMetaClass();
//获取可读写字段
auto rw = cls->data();
//获取只读字段
auto ro = rw->ro;

// Install methods and properties that the class implements itself.
// 加载类自身实现的方法和属性
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list/*自身实现的方法列表*/, 1, YES, isBundleClass(cls));
//将ro->baseMethods方法添加到rw->methods
rw->methods.attachLists(&list, 1);
}

property_list_t *proplist = ro->baseProperties;
if (proplist) {
//将ro->baseProperties方法添加到rw->properties
rw->properties.attachLists(&proplist, 1);
}

protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
//将ro->baseProtocols方法添加到rw->protocols
rw->protocols.attachLists(&protolist, 1);
}

// Root classes get bonus method implementations if they don't have
// them already. These apply before category replacements.
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
}

// Attach categories.
// 获取未添加的分类
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
// 添加分类
attachCategories(cls, cats, false /*don't flush caches*/);

//.....

if (cats) free(cats);
//.....
}

methodizeClass方法则进一步初始化rw,这一步将ro中的属性,协议,方法拷贝到rw,以及将分类中对应的属性,协议,方法也拷贝到class中的rw对应字段。

回顾整个map_image阶段就是在镜像实例话结束后,通过读取_DATA中的指定session数据,来读出整个镜像中各个class的关键数据,并构建objc_class数据结构对象,通过从_DATA中读取的数据来填充objc_class来完成整个类的实例化。

  • load_images

load方法是我们在日常开发中可以接触到的调用时间最靠前的方法,它的调用不是惰性的,在主函数运行之前,load 方法就会调用,并且它只会在程序调用期间调用一次,最重要的是,如果在类与分类中都实现了 load 方法,它们都会被调用,
不像其它的在分类中实现的方法会被覆盖,但是在使用load方法的时候需要注意,由于load方法的运行时间过早,所以可能不是一个理想的环境,因为它不能保证某些类可能需要在在其它类之前加载,但是在这个时间点,所有的 framework 都已经加载到了运行时中,所以调用 framework 中的方法都是安全的,同时需要注意的是重载Class 的 +load 方法时不能调父类super,我们来看下这部分逻辑:

void
load_images(const char *path __unused, const struct mach_header *mh)
{
//......
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
//prepare_load_methods方法中对load方法进行了前期准备
prepare_load_methods((const headerType *)mh);
}
// 调用+load方法
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}

load_images 方法中主要调用了prepare_load_methods 以及 call_load_methods

void prepare_load_methods(const headerType *mhdr)
{
//....
//获取类
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}

//添加分类
category_t **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
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
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
//分析这段代码,可以知道,在将子类添加到加载列表之前,其父类一定会优先加载到列表中。
//这也是为何父类的+load方法在子类的+load方法之前调用的根本原因。
schedule_class_load(cls->superclass);
//将类添加到可加载列表
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}

prepare_load_methods 以及 schedule_class_load方法会按照父类,子类,分类的顺序将需要调用load方法的class添加到loadable_list中去

void add_class_to_loadable_list(Class cls)
{
IMP method;

loadMethodLock.assertLocked();

//该类是否有load方法
method = cls->getLoadMethod();
if (!method) return; // Don't bother if cls has no +load method

//........
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));
}

loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
void add_category_to_loadable_list(Category cat)
{
IMP method;

loadMethodLock.assertLocked();

//该分类是否有load方法
method = _category_getLoadMethod(cat);

if (!method) return;

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));
}

loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}

调用类以及分类中的load方法

/***********************************************************************
* call_load_methods
* Call all pending class and category +load methods.
* Class +load methods are called superclass-first.
* Category +load methods are not called until after the parent class's +load.
*
* This method must be RE-ENTRANT, because a +load could trigger
* more image mapping. In addition, the superclass-first ordering
* must be preserved in the face of re-entrant calls. Therefore,
* only the OUTERMOST call of this function will do anything, and
* that call will handle all loadable classes, even those generated
* while it was running.
*
* The sequence below preserves +load ordering in the face of
* image loading during a +load, and make sure that no
* +load method is forgotten because it was added during
* a +load call.
* Sequence:
* 1. Repeatedly call class +loads until there aren't any more
* 2. Call category +loads ONCE.
* 3. Run more +loads if:
* (a) there are more classes to load, OR
* (b) there are some potential category +loads that have
* still never been attempted.
* Category +loads are only run once to ensure "parent class first"
* ordering, even if a category +load triggers a new loadable class
* and a new loadable category attached to that class.
*
* Locking: loadMethodLock must be held by the caller
* All other locks must not be held.
**********************************************************************/
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) {
//调用类的load方法
call_class_loads();
}

// 2. Call category +loads ONCE
//调用分类的load方法
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;
}
static void call_class_loads(void)
{
// Detach current loadable list.
struct loadable_class *classes = loadable_classes;/*这是preppare阶段构造的*/
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方法
(*load_method)(cls, SEL_load);
}

// Destroy the detached list.
if (classes) free(classes);
}
static bool call_category_loads(void)
{
int i, shift;
bool new_categories_added = NO;

// Detach current loadable list.
struct loadable_category *cats = loadable_categories;/*数据来源 在prepare阶段构建*/
int used = loadable_categories_used;
int allocated = loadable_categories_allocated;
loadable_categories = nil;
loadable_categories_allocated = 0;
loadable_categories_used = 0;

// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
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_method)(cls, SEL_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;
}

我们在这篇博客的最后对整个过程进行一个简单的总结:

在应用启动的时候会从镜像中查找dyld的地址,而后将dyld加载进来,找到dyld的入口地址__dyld_start,并将后续工作交给 dyld 负责:

  1. dyld 开始将程序二进制文件实例化为一个ImageLoader
  2. 交由 ImageLoader 读取 image 其中包含了我们的类、方法等各种符号,以及根据Mach-O的Load Commands段加载所有依赖的动态库并完成链接,初始化工作。在主程序初始化阶段,runtime会向dyld绑定回调。
  3. 当image加载到内存后,dyld 会通知 runtime 进行处理,runtime 接手后调用 map_images 做解析和处理,接下来 load_images 中调用 call_load_methods 方法,
    遍历所有加载进来的 Class,按继承层级依次调用 Class 的 +load 方法和其 Category 的 +load 方法
  4. 当map_images以及load_images执行完毕之后可执行文件中和动态库所有的符号(Class,Protocol,Selector,IMP,…)都已经按格式成功加载到内存中,被 runtime 所管理。
  5. 所有初始化工作结束后,dyld 调用真正的 main 函数,紧接着dyld 会清理现场,将调用栈回归,只剩下main函数。
Contents
  1. 1. map_images
  2. 2. load_images