应用程序加载(一) -- dyld流程分析
应用程序加载(二) -- dyld&objc关联以及类的加载初探
应用程序加载(三)-- 类的加载
应用程序加载(四)-- 分类的加载
应用程序加载(五)-- 类扩展和关联对象
开场白
上篇文章研究到_read_images
中调用了readClass
函数。这篇继续开始我们的研究
readClass
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->mangledName();
//省略不关心的代码
...
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// ASSERT(cls == getClass(name));
ASSERT(getClassExceptSomeSwift(mangledName));
} else {
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
}
//省略不关心的代码
...
return cls;
}
//获取类名
- 从类中获取类名,后续将类信息加入到哈希表中,类名作为key。获取类名的实现如图:
-
调用函数
addNamedClass(cls, mangledName, replacing);
,把类和类名作为参数传入进去,实现如图:
看看加入表的代码实现:
void *NXMapInsert(NXMapTable *table, const void *key, const void *value) {
MapPair *pairs = (MapPair *)table->buckets;
unsigned index = bucketOf(table, key);
MapPair *pair = pairs + index;
if (key == NX_MAPNOTAKEY) {
_objc_inform("*** NXMapInsert: invalid key: -1\n");
return NULL;
}
unsigned numBuckets = table->nbBucketsMinusOne + 1;
//哈希表中没有 直接加入
if (pair->key == NX_MAPNOTAKEY) {
pair->key = key; pair->value = value;
table->count++;
if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
return NULL;
}
if (isEqual(table, pair->key, key)) {//表中有,并且key能匹配上,直接修改原来记录的value
const void *old = pair->value;
if (old != value) pair->value = value;/* avoid writing unless needed! */
return (void *)old;
} else if (table->count == numBuckets) {
/* no room: rehash and retry */
_NXMapRehash(table);
return NXMapInsert(table, key, value);
} else {//key匹配不上,哈希冲突,重新计算哈希并加入到表
unsigned index2 = index;
while ((index2 = nextIndex(table, index2)) != index) {
pair = pairs + index2;
if (pair->key == NX_MAPNOTAKEY) {
pair->key = key; pair->value = value;
table->count++;
if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
return NULL;
}
if (isEqual(table, pair->key, key)) {
const void *old = pair->value;
if (old != value) pair->value = value;/* avoid writing unless needed! */
return (void *)old;
}
}
/* no room: can't happen! */
_objc_inform("**** NXMapInsert: bug\n");
return NULL;
}
}
代码中有涉及到两个结构体:
NXMapTable
和MapPair
-
MapPair
结构体有两个成员,分别是key
和value
-
NXMapTable
结构体中相对复杂一些,如图
NXMapInsert
实现中,先从NXMapTable
中获取buckets
,并且强转成MapPair*
类型-
获取到最大偏移量的位置,进行判断
- 如果该位置上没有值,直接进行存储。之前获取的类名作为
key
,而value
就是类的信息cls
- 位置上有值,并且与传入的参数
key
相等,直接替换old value
- 如果不相等,说明哈希冲突,重新计算一个位置再进行存储
- 如果该位置上没有值,直接进行存储。之前获取的类名作为
到此处,仅仅是将类名和类的总体信息保存到系统的一个表中。下面了解一下类中方法做了什么处理。
realizeClassWithoutSwift
在_read_images
中,会看到调用函数realizeClassWithoutSwift
的地方
- 注释中有一个关键字
non-lazy classes
,意思是:当实现了+load
方法,那么这个类就是非懒加载类。 - 相反,如果不实现
+load
方法,就是懒加载类。懒加载类是在方法查找时调用的。其实很好理解,既然要查找类的方法,那么首先类必须得存在。
懒加载类和非懒加载类的函数调用栈,如图:
接下来看看realizeClassWithoutSwift
的实现,代码比较长,通过多张图片的方式查看:
- 首先根据传入的参数
cls
,获取ro
。 - 然后用给临时变量
rw
开辟空间,并且将ro
赋值给rw
中。 - 最后讲
rw
赋值给cls
中
- 既然要实现类,那么也要完善他继承关系和元类的相关信息,所以此处进行递归
- 后续代码就是对
元类
和non-pointer isa
等相关的处理了,这部分代码就不贴了,感兴趣可自行查看
- 最后调用了
methodizeClass
函数。注意:由于递归的原因,此处的cls
可能是父类或者元类。
methodizeClass
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
const char *mangledName = cls->mangledName();
//省略代码
...
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
if (rwe) rwe->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
//省略代码
...
}
- 通过源码能够看到,分别处理了方法、属性、协议
- 处理方法的时候,调用了
prepareMethodLists
函数
static void
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle)
{
runtimeLock.assertLocked();
const char *mangledName = cls->mangledName();
if (addedCount == 0) return;
//省略代码
...
// Add method lists to array.
// Reallocate un-fixed method lists.
// The new methods are PREPENDED to the method list array.
for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[i];
ASSERT(mlist);
// Fixup selectors if necessary
if (!mlist->isFixedUp()) {
fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
}
}
//省略代码
...
}
static void
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
runtimeLock.assertLocked();
ASSERT(!mlist->isFixedUp());
// fixme lock less in attachMethodLists ?
// dyld3 may have already uniqued, but not sorted, the list
if (!mlist->isUniqued()) {
mutex_locker_t lock(selLock);
// Unique selectors in list.
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name);
meth.name = sel_registerNameNoLock(name, bundleCopy);
}
}
// Sort by selector address.
if (sort) {
method_t::SortBySELAddress sorter;
std::stable_sort(mlist->begin(), mlist->end(), sorter);
}
// Mark method list as uniqued and sorted
mlist->setFixedUp();
}
-
methodizeClass
中调用了fixupMethodList
-
fixupMethodList
实现中对方法的sel
进行了一个排序处理。在之前的文章方法调用(二)-- 慢速查找流程中,方法查找时使用了二分查找。而二分查找必须是有序的。这也是此处进行排序的原因。 - 注意注释
Sort by selector address.
,是按照sel的地址进行排序的。