前言
在前面一章节中我们探究了ios启动加载,当时探究到了apple会在_objc_init
函数调用_dyld_objc_notify_register
函数并且传递了&map_images与load_images等参数,在镜像文件初始化之后,就会调用map_images函数.然后接着就会调用load_images,接下来我们今天先探究一个map_imags
函数的内容。
探究map_images
void map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
/*在老的运行时代码中使用的是recursive_mutex_locker_t lock(loadMethodLock);递归锁*/
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
很明显在map_images函数中,直接返回了一个map_images_nolock函数调用,所以我们直接阅读map_images_nolock
函数源码.
1.1 preopt_init与sel_init
在map_images_nolock首先通过调用执行preopt_init()
函数禁用dyld共享缓存对SEL的优化,因为dyld共享缓存中的选择器是不可信的。
void preopt_init(void) {
省略...
disableSharedCacheOptimizations();
省略...
}
//sel_init()决定dyld共享缓存中的选择器是不可信的
void disableSharedCacheOptimizations(void)
{
fixed_up_method_list = OBJC_FIXED_UP_outside_dyld;
}
所以在map_images_nolock当中调用了sel_init()
函数,初始化内部使用的选择器表和注册了c++的构造与析构方法
/***********************************************************************
* sel_init
* Initialize selector tables and register selectors used internally.
**********************************************************************/
void sel_init(size_t selrefCount)
{
namedSelectors.init((unsigned)selrefCount);
// Register selectors used by libobjc
mutex_locker_t lock(selLock);
SEL_cxx_construct = sel_registerNameNoLock(".cxx_construct", NO);
SEL_cxx_destruct = sel_registerNameNoLock(".cxx_destruct", NO);
}
1.2 arr_init
在map_images_nolock当中通过调用arr_init函数,完成了自动释放池页
、包含引用计数表与弱引用表的散列表
、关联对象管理表
的初始化
void arr_init(void) {
AutoreleasePoolPage::init();//自动释放池页初始化
SideTablesMap.init();//散列表初始化
_objc_associations_init();//关联对象管理表初始化
}
先简单的看一下SideTablesMap的结构如下,以后再探究
static objc::ExplicitInit> SideTablesMap;
struct SideTable {
spinlock_t slock; //自旋锁
RefcountMap refcnts;//引用计数表
weak_table_t weak_table;//弱引用表
省略...
}
1.3 _read_images
前面一系列初始化准备工作完成之后就进入到了read_images
流程。
在read_images中
1.3.1 条件控制进行一次的加载,针对旧的swift版本禁用nonpointer isa
,初始化标记指针(tagged pointer)
,创建类的映射表
。
//始化标记指针(tagged pointer)
initializeTaggedPointerObfuscator();
//创建类的映射表
int namedClassesSize = (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
1.3.2 修复编译阶段的@selector
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
//是否有dyld预优化过的sel
if (hi->hasPreoptimizedSelectors()) 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]);
/*在Mac OS X 10.15或者iOS 13.0及以上版本中,sel_registerNameNoLock 本质是调用_dyld_get_objc_selector*/
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel) {
sels[i] = sel;
}
}
}
}
ts.log("IMAGE TIMES: fix up selector references");
其实就是修复一些名称相同的SEL的地址,
1.3.3 错误混乱的类处理
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
//发现类。修复未解决的未来类。
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) {
if (! mustReadClasses(hi, hasDyldRoots)) {
//镜像已充分优化,无需调用readClass()
continue;
}
classref_t const *classlist = _getObjc2ClassList(hi, &count);
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
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");
记录被移动但是未被删除的类,留待将来解决。
1.3.4 重新映射未重映射懒加载类
// 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]);//重新映射
}
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);//重新映射父类
}
}
}
ts.log("IMAGE TIMES: remap classes");
//remapClassRef底层调用remappedClasses方法,只对lazy类处理
/*
*为已实现的未来类返回oldClass => newClass映射。
*被忽略的弱链接类返回oldClass => nil映射。
*/
static objc::DenseMap *remappedClasses(bool create) {
static objc::LazyInitDenseMap remapped_class_map;
runtimeLock.assertLocked();
// start big enough to hold CF's classes and a few others
return remapped_class_map.get(create, 32);
}
重新映射未重映射的懒加载类,也就是未实现+load方法
的类,
1.3.5 修复一些消息
// Fix up old objc_msgSend_fixup call sites
//修复旧的objc_msgSend_fixup调用站点
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
if (PrintVtables) {
_objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
"call sites in %s", count, hi->fname());
}
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
}
}
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
static void fixupMessageRef(message_ref_t *msg) {
msg->sel = sel_registerName((const char *)msg->sel);
if (msg->imp == &objc_msgSend_fixup) {
if (msg->sel == @selector(alloc)) {
msg->imp = (IMP)&objc_alloc;
} else if (msg->sel == @selector(allocWithZone:)) {
msg->imp = (IMP)&objc_allocWithZone;
} else if (msg->sel == @selector(retain)) {
msg->imp = (IMP)&objc_retain;
} else if (msg->sel == @selector(release)) {
msg->imp = (IMP)&objc_release;
} else if (msg->sel == @selector(autorelease)) {
msg->imp = (IMP)&objc_autorelease;
} else {
msg->imp = &objc_msgSend_fixedup;
}
}
else if (msg->imp == &objc_msgSendSuper2_fixup) {
msg->imp = &objc_msgSendSuper2_fixedup;
}
else if (msg->imp == &objc_msgSend_stret_fixup) {
msg->imp = &objc_msgSend_stret_fixedup;
}
else if (msg->imp == &objc_msgSendSuper2_stret_fixup) {
msg->imp = &objc_msgSendSuper2_stret_fixedup;
}
#if defined(__i386__) || defined(__x86_64__)
else if (msg->imp == &objc_msgSend_fpret_fixup) {
msg->imp = &objc_msgSend_fpret_fixedup;
}
#endif
#if defined(__x86_64__)
else if (msg->imp == &objc_msgSend_fp2ret_fixup) {
msg->imp = &objc_msgSend_fp2ret_fixedup;
}
#endif
}
修复一些方法,比如alloc,objc_retain等方法,因为不同的系统方法的实现是不一样,底层的库实现也不一样,所以需要在加载的时候,进行修复。
1.3.6 当我们类里面有协议的时候:readProtocol
// 发现协议并且读取
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();
// Skip reading protocols if this is an image from the shared cache
// and we support roots
// Note, after launch we do need to walk the protocol as the protocol
// in the shared cache is marked with isCanonical() and that may not
// be true if some non-shared cache binary was chosen as the canonical
//如果是从共享缓存读取的image,跳过读取协议的过程
if (launchTime && isPreoptimized) {
if (PrintProtocols) {
_objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
hi->fname());
}
continue;
}
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);
}
}
ts.log("IMAGE TIMES: discover protocols");
通过readProtocol
函数读取协议。具体实现后面再探究
1.3.7 修复协议引用
// Fix up @protocol references
//优化过的imags协议引用可能是正确的,但是我们不确定。
for (EACH_HEADER) {
//在启动的时候,我们知道优化过的images的指向,
//共享缓存定义的协议,我们不需要检查
if (launchTime && hi->isPreoptimized())
continue;
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
//修复协议引用,以防协议被重新分配。
remapProtocolRef(&protolist[i]);
}
}
ts.log("IMAGE TIMES: fix up @protocol references");
1.3.8 分类处理
// Discover categories. Only do this after the initial category
// attachment has been done. For categories present at startup,
// discovery is deferred until the first load_images call after
// the call to _dyld_objc_notify_register completes. rdar://problem/53119145
/发现类别。只有在初始类别之后才这样做
//附件已完成。对于启动时的类别,
//发现延迟到第一个load_images调用之后
//调用_dyld_objc_notify_register完成。
if (didInitialAttachCategories) {
for (EACH_HEADER) {
load_categories_nolock(hi);
}
}
// Category discovery MUST BE Late to avoid potential races
// when other threads call the new category code before
// this thread finishes its fixups.
// +load handled by prepare_load_methods()
ts.log("IMAGE TIMES: discover categories");
1.3.9 非懒加载类的加载处理
// Realize non-lazy classes (for +load methods and static instances)
//实现非懒加载类的加载。(用于+load 方法和静态实例)
for (EACH_HEADER) {
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);//重映射类
if (!cls) continue;
addClassTableEntry(cls);//添加到类表中
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
//修正了不允许重定位类的问题
}
//类的加载
realizeClassWithoutSwift(cls, nil);
}
}
ts.log("IMAGE TIMES: realize non-lazy classes");
1.3.10 没有被处理的类,优化被侵犯的类
// Realize newly-resolved future classes, in case CF manipulates them
//实现新解析的未来类,以防CF操纵它们
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);
}
ts.log("IMAGE TIMES: realize future classes");
总结
在map_images中前期主要就是对方法选择器、类、消息、协议的修复
。以及非懒加载类与分类的信息的加载(协议,方法,成员变量)
。
下一篇文章准备探究类与分类的加载。