iOS开发进阶:从dyld到main
问自己两个问题:
1.应用启动在main
函数之前到底做了什么事情?
2.类、分类中load
方法的加载顺序怎样的?分类中出现的与主类同名的方法,会调用哪一个呢?
这些问题,不跟踪一次底层的源码?怎么会领悟得透彻呢?
我们实现一个类,然后在load
方法中打个断点看看:
这个调用栈的信息就非常丰富,从dyld-_dyld_start
开始,经历了一系列步骤,最终进入了load_images
,在load_images
方法中调用了load
方法。
我们到Apple Open Source网站下载一份最新的dyld
源码、system
源码、 dispatch
源码、objc
源码。(目前最新版本分别是dyld-852.2
,Libsystem-1292.120.1
,libdispatch-1271.120.2
,objc4-824
),然后就可以根据load
断点打印的调用栈来一点点跟。
一、dyld内部的主要流程
__dyld_start
开始于dyldStartup.s
汇编代码中:
__dyld_start调用main
这里标注了__dyld_start
调用了dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
开始初始化,最后调用了main
函数进入主程序。
所以重点就在dyldbootstrap::start
流程中了,由于步骤较多,我们就通过流程图来表示,具体如下:
简单来说:
main
之前通过,dyld
对主程序运行环境进行初始化,生成ImageLoader
把动态库生成的image
加载到内存中,然后进行链接绑定,接着初始化所有动态库,再接着初始化libSystem
,libdispatch
,libobjc
。其中libobjc
的初始化函数_objc_init
,初始化运行环境,并且通过_dyld_objc_notify_register(&map_images, load_images, unmap_image)
注册读取images,加载images的函数。
二、map_images流程
2.1map_images
主要用以处理dyld
给出的image
,该函数直接进入了map_images_nolock
函数。接下来我们先看看这个函数主要作恶什么事情:
void map_images_nolock(unsigned mhCount, const char * const mhPaths[], const struct mach_header * const mhdrs[]) {
if (firstTime) {
/*首次执行作一些必要的初始化*/
preopt_init();
}
/*通过image的地址,创建header_info信息,并且添加到hList中*/
/*mach_header对应每个动态库的macho*/
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
hList[hCount++] = hi;
if (firstTime) {
/*namedSelectors初始化*/
sel_init(selrefCount);
/*AutoreleasePoolPage::init();//自动释放池初始化
SideTablesMap.init();//散列表初始化
_objc_associations_init();//关联表初始化
*/
arr_init();
}
if (hCount > 0) {
/*准备工作完成后就会进入_read_images*/
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
}
2.2 接下来在看_read_images
的流程
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses){
/*先看一下这个宏定义,他是for循环的条件语句,每次取出hi = hList[hIndex],并且hIndex++。所以在下面会看到EACH_HEADER的写法*/
#define EACH_HEADER \
hIndex = 0; \
hIndex < hCount && (hi = hList[hIndex]); \
hIndex++
/*1.初始化TaggedPointer, 创建gdb_objc_realized_classes*/
if (!doneOnce) {
doneOnce = true;
if (DisableTaggedPointers) {
disableTaggedPointers();
}
initializeTaggedPointerObfuscator();
int namedClassesSize = (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
}
/*2.Fix up @selector references*/
static size_t UnfixedSelectors;
{
for (EACH_HEADER) {
SEL *sels = _getObjc2SelectorRefs(hi, &count);
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
/*namedSelectors.get().insert(name)*/
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel) {
sels[i] = sel;
}
}
}
}
/*3.Discover classes. Fix up unresolved future classes. Mark bundle classes*/
for(EACH_HEADER){
classref_t const *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[I];
/*readClass:旧类改动后会生成新的类,并重映射到新的类上*/
/*mangledName !=nil: NXMapInsert(nonMetaClasses(), cls->ISA(), cls) || NXMapInsert(gdb_objc_realized_classes, name, cls)*/
/*mangledName ==ni:类自身和元类allocatedClasses.insert(cls)*/
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
if (newCls != cls && newCls) {
resolvedFutureClasses = (Class *)realloc(resolvedFutureClasses, (resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
/*4.Fix up remapped classes*/
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]);
}
}
}
/*5.Fix up old objc_msgSend_fixup call sites*/
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
}
}
/*6.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->hasPreoptimizedProtocols();
bool isBundle = hi->isBundle();
protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map, isPreoptimized, isBundle);
}
}
//Fix up @protocol references
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
/* 7.Discover categories. Only do this after the initial category attachment has been done*/
if (didInitialAttachCategories) {
for (EACH_HEADER) {
/*cls->isStubClass():objc::unattachedCategories.addForClass(lc, cls);*/
/*First, register the category with its target class. Then, rebuild the class's method lists (etc) if the class is realized*/
load_categories_nolock(hi);
}
}
/*8.Realize non-lazy classes (for +load methods and static instances)*/
for (EACH_HEADER) {
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
addClassTableEntry(cls);
realizeClassWithoutSwift(cls, nil);
}
}
/*9.Realize newly-resolved future classes, in case CF manipulates them*/
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
realizeClassWithoutSwift(cls, nil);
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
free(resolvedFutureClasses);
}
}
static void load_categories_nolock(header_info *hi) {
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
size_t count;
auto processCatlist = [&](category_t * const *catlist) {
for (unsigned i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
locstamped_category_t lc{cat, hi};
if (!cls) {
// Category's target class is missing (probably weak-linked). Ignore the category.
continue;
}
// Process this category.
if (cls->isStubClass()) {
/* Stub classes are never realized. Stub classes don't know their metaclass until they're initialized, so we have to add categories with class methods or properties to the stub itself. methodizeClass() will find them and add them to the metaclass as appropriate.*/
if (cat->instanceMethods ||
cat->protocols ||
cat->instanceProperties ||
cat->classMethods ||
cat->protocols ||
(hasClassProperties && cat->_classProperties))
{
objc::unattachedCategories.addForClass(lc, cls);
}
} else {
/* First, register the category with its target class. Then, rebuild the class's method lists (etc) if the class is realized.*/
if (cat->instanceMethods || cat->protocols || cat->instanceProperties) {
/*实例方法/实例属性:class本身已经实现了,会将相关方法属性添加到class,否则添加到unattachedCategories*/
if (cls->isRealized()) {
attachCategories(cls, &lc, 1, ATTACH_EXISTING);
}else {
objc::unattachedCategories.addForClass(lc, cls);
}
}
if (cat->classMethods || cat->protocols || (hasClassProperties && cat->_classProperties)){
/*类方法/类属性:class-isa本身已经实现了,会将相关方法属性添加到metadata-class,否则添加到unattachedCategories*/
if (cls->ISA()->isRealized()) {
attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
} else {
objc::unattachedCategories.addForClass(lc, cls->ISA());
}
}
}
}
};
processCatlist(hi->catlist(&count));
processCatlist(hi->catlist2(&count));
}
三、load_images流程
入口函数,准备load方法,调用load方法
void load_images(const char *path __unused, const struct mach_header *mh){
prepare_load_methods((const headerType *)mh);// Discover +load methods
call_load_methods(); // Call +load methods (without classLock - re-entrant)
}
3.1准备所有的load方法,把包含load方法的class,category添加到对应的表中
void prepare_load_methods(const headerType *mhdr){
size_t count, i;
runtimeLock.assertLocked();
/*superclsss->class:添加到loadable_classes表中,实体类型loadable_class*/
classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
/*category:添加到loadable_categories表中,实体类型loadable_category*/
category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
add_category_to_loadable_list(categorylist[i]);
}
}
class添加到loadable_classes表中,加入的是loadable_class类型(包含cls和load-method, getLoadMethod就是查找load方法);分类添加到loadable_categories表中,加入的是loadable_category类型(包含category和load-method)
static void schedule_class_load(Class cls){
...
/*先插入superclass, 再插入class!!load方法的调用顺序*/
schedule_class_load(cls->getSuperclass());
add_class_to_loadable_list(cls);
...
}
/*没有load方法的类不会加进来,会在已经申请的内存用完的情况继续扩容,loadable_classes_used作为计数器*/
void add_class_to_loadable_list(Class cls){
IMP 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++;
}
/*没有load方法的类不会加进来,会在已经申请的内存用完的情况继续扩容,loadable_categories_used作为计数器*/
void add_category_to_loadable_list(Category cat){
IMP method = _category_getLoadMethod(cat);
if (!method) return; // Don't bother if cat has no +load method
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++;
}
/* loadable_class结构体*/
struct loadable_class {
Class cls; // may be nil
IMP method;
};
/* loadable_category结构体*/
struct loadable_category {
Category cat; // may be nil
IMP method;
};
3.2调用load方法
void call_load_methods(void){
...
do {
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);
...
}
//调用class-load方法,loadable_classes_used=0
static void call_class_loads(void){
...
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;
(*load_method)(cls, @selector(load));
}
...
}
//调用category-load方法,loadable_categories_used=0
static bool call_category_loads(void){
...
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()) {
(*load_method)(cls, @selector(load));
cats[i].cat = nil;
}
}
...
}