在上一篇文章中,我们探索了dyld的加载流程,dyld在初始化动态库的时候程序在_objc_init中通过_dyld_objc_notify_register()调用map_images和load_images这两个函数。接下来我们就来研究一下map_images和load_images这两个函数都做了什么操作。
上一篇文章中我们也知道_objc_init初始化的时候,还进行了很多的init操作。
void _objc_init(void) {
// fixme defer initialization until an objc-using image is found?
environ_init(); // 环境变量的初始化
tls_init(); // 为每一条线程创建析构函数 tls:线程的局部存储
static_init(); // 运行c++的静态构造函数
runtime_init(); // 初始化分类和类表
exception_init(); // 初始化异常处理系统 Called by map_images()
#if __OBJC2__
cache_t::init(); // 初始化缓存
#endif
_imp_implementationWithBlock_init(); // mac os 处理
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
我们先来看一下这些初始化操作都做了什么
environ_init
void environ_init(void) {
bool PrintHelp = false;
bool PrintOptions = false;
bool maybeMallocDebugging = false;
for (char **p = *_NSGetEnviron(); *p != nil; p++) {
......
if (0 == strncmp(*p, "OBJC_HELP=", 10)) {
PrintHelp = true;
continue;
}
if (0 == strncmp(*p, "OBJC_PRINT_OPTIONS=", 19)) {
PrintOptions = true;
continue;
}
......
}
......
// Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
if (PrintHelp || PrintOptions) {
if (PrintHelp) {
_objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
_objc_inform("OBJC_HELP: describe available environment variables");
if (PrintOptions) {
_objc_inform("OBJC_HELP is set");
}
_objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
}
if (PrintOptions) {
_objc_inform("OBJC_PRINT_OPTIONS is set");
}
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
}
}
}
我们可以看到这里处理的是我们配置的一些环境变量,我们可以在工程的Scheme的Arguments中加入变量看一下控制台的打印日志
我们看到这里打印的是我们环境变量的配置信息,我们可以在这里获取想要改变的环境变量,然后在
Scheme进行修改,如想要关闭指针优化就在Scheme中配置OBJC_DISABLE_NONPOINTER_ISA:YES即可,还有打印所有实现+load方法的类或分类的配置OBJC_PRINT_LOAD_METHODS,打印dyld加载的所有imageOBJC_PRINT_IMAGES等等,
runtime_init
void runtime_init(void) {
objc::unattachedCategories.init(32); // 初始化分类表
objc::allocatedClasses.init(); // 初始化类表
}
初始化分类及类的表,对于分类及类的加载就会在这两张表中进行插入数据。
load_images
先来看一下其内部源码
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();
}
很简单,加载所有分类,查找load方法,执行所有load方法,我们先看一下怎么查找load方法的
void prepare_load_methods(const headerType *mhdr) {
size_t count, i;
runtimeLock.assertLocked();
// 获取所有的非懒加载类
classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
// 获取所有的非懒加载分类
category_t * const *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
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
realizeClassWithoutSwift(cls, nil);
ASSERT(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
先获取所有的非懒加载类,类分为懒加载类与非懒加载类,只要重写load方法都是非懒加载类或非懒加载分类,类通过schedule_class_load递归调用其父类的schedule_class_load,然后使用add_class_to_loadable_list将类添加到loadable_classes中,每个元素是一个名为loadable_class结构体,其中包含了类和其load方法IMP。
void add_class_to_loadable_list(Class cls) {
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod(); // 获取load方法实现
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load", cls->nameForLogging());
}
// 扩容
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));
}
// 保存类及load方法
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++; //指针指向下一地址
}
类的load方法找完后,再找分懒加载分类的load方法,add_category_to_loadable_list将分类添加到loadable_categories中,每个元素是一个名为loadable_categories结构体,其中包含了分类和其load方法IMP,和类的区别就是,类会递归调用父类,分类只会调用本类。下面我们来看一下call_load_methods()是如果执行load方法的
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;
}
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);
}
static bool call_category_loads(void){
int i, shift;
bool new_categories_added = NO;
// Detach current loadable list.
struct loadable_category *cats = loadable_categories;
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, @selector(load));
cats[i].cat = nil;
}
}
......
return new_categories_added;
}
在这个方法中,对获取到的类和分类两个list进行遍历,由于我们在存储的时候已经存储了IMP,所以可以直接使用IMP进行调用load方法。在类的列表中,由于结构体的一个元素就是class,另一个就是load的IMP,所以使用(*load_method)(cls, @selector(load))就是直接调用类的load的方法。而在分类中,就是通过分类拿到类,然后再进行调用。
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[]) {
static bool firstTime = YES;
header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;
// 共享缓存的优化处理
if (firstTime) {
preopt_init();
}
......
// Find all images with Objective-C metadata.
hCount = 0;
// 统计所以的类
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) { ...... }
}
if (firstTime) {
sel_init(selrefCount); // 初始化c++的构造和析构函数
arr_init(); //初始化自动释放池、散列表、关联对象
}
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
// Call image load funcs after everything is set up.
for (auto func : loadImageFuncs) {
for (uint32_t i = 0; i < mhCount; i++) {
func(mhdrs[i]);
}
}
内部其实是调用map_images_nolock,第一次进入会先去执行preopt_init,其内部就是优化共享缓存的处理
void preopt_init(void){
......
const uintptr_t start = (uintptr_t)_dyld_get_shared_cache_range(&length);
if (start) {
objc::dataSegmentsRanges.setSharedCacheRange(start, start + length);
}
// `opt` not set at compile time in order to detect too-early usage
const char *failure = nil;
opt = &_objc_opt_data; //共享缓存的处理
if (DisablePreopt) { // 环境变量,关闭dyld共享缓存优化
// OBJC_DISABLE_PREOPTIMIZATION is set
// If opt->version != VERSION then you continue at your own risk.
failure = "(by OBJC_DISABLE_PREOPTIMIZATION)";
}
......
if (failure) {
// All preoptimized selector references are invalid.
preoptimized = NO;
opt = nil;
disableSharedCacheOptimizations();
} else {
// Valid optimization data written by dyld shared cache
preoptimized = YES;
}
}
接下来map_images_nolock使用循环统计所有的类,再次初始化c++的构造和析构函数,初始化自动释放池、散列表、关联对象,然后调用_read_images
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses) {
header_info *hi;
uint32_t hIndex;
size_t count;
size_t i;
Class *resolvedFutureClasses = nil;
size_t resolvedFutureClassCount = 0;
static bool doneOnce;
bool launchTime = NO;
TimeLogger ts(PrintImageTimes);
runtimeLock.assertLocked();
#define EACH_HEADER \
hIndex = 0; \
hIndex < hCount && (hi = hList[hIndex]); \
hIndex++
if (!doneOnce) {
doneOnce = YES;
launchTime = YES;
...开启指针优化...
if (DisableTaggedPointers) { // 关闭NSNumber、NSString等小对象 的Tagged Pointer优化
disableTaggedPointers();
}
initializeTaggedPointerObfuscator(); // Tagged Pointer 处理
// 创建 gdb_objc_realized_classes 表,存放dyld share cache的数据
int namedClassesSize = (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
ts.log("IMAGE TIMES: first time tasks");
}
// Fix up @selector references
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
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]);
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel) {
sels[i] = sel;
}
}
}
}
ts.log("IMAGE TIMES: fix up selector references");
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) {
……
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");
......
ts.log("IMAGE TIMES: discover categories");
ts.log("IMAGE TIMES: realize non-lazy classes");
ts.log("IMAGE TIMES: realize future classes");
if (DebugNonFragileIvars) {
realizeAllClasses();
}
我们现在使用的是虚拟内存(ASLR),需要dyld进行rebase和binding操作来修复我们镜像的资源指针,指向正确的内存地址,_read_images首先doneOnce只调用一次,内部开启指针优化和Tagged Pointer处理,并创建一张存放dyld共享缓存数据的表gdb_objc_realized_classes;接下来会去修复selector,将指向错误的内存地址的sel修复成正确的sel内存地址,这个过程就是rebase的过程,将虚拟内存地址换成真实内存地址,下面继续修复protocol、objc_msgSend_fixup等;然后初始化所有非懒加载类,进行rw、ro等操作;遍历已标记的懒加载类,并作初始化操作;处理所以分类,包括类和元类;初始化所有未初始化的类。
这其中有个很重要的过程,Discover classes,发现所有的类,其内部使用readClass进行类的获取
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized) {
const char *mangledName = cls->nonlazyMangledName();
if (missingWeakSuperclass(cls)) {
……
}
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
if (mangledName != nullptr) {
if (Class newCls = popFutureNamedClass(mangledName)) {
if (newCls->isAnySwift()) {
……
}
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro();
memcpy(newCls, cls, sizeof(objc_class));
// Manually set address-discriminated ptrauthed fields
// so that newCls gets the correct signatures.
newCls->setSuperclass(cls->getSuperclass());
newCls->initIsa(cls->getIsa());
rw->set_ro((class_ro_t *)newCls->data());
newCls->setData(rw);
freeIfMutable((char *)old_ro->getName());
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
}
if (headerIsPreoptimized && !replacing) {
......
} else {
if (mangledName) { //some Swift generic classes can lazily generate their names
addNamedClass(cls, mangledName, replacing);
} else {
Class meta = cls->ISA();
const class_ro_t *metaRO = meta->bits.safe_ro();
ASSERT(metaRO->getNonMetaclass() && "Metaclass with lazy name must have a pointer to the corresponding nonmetaclass.");
ASSERT(metaRO->getNonMetaclass() == cls && "Metaclass nonmetaclass pointer must equal the original class.");
}
addClassTableEntry(cls);
}
// for future reference: shared cache never contains MH_BUNDLEs
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}
static void addClassTableEntry(Class cls, bool addMeta = true) {
runtimeLock.assertLocked();
// This class is allowed to be a known class via the shared cache or via
// data segments, but it is not allowed to be in the dynamic table already.
auto &set = objc::allocatedClasses.get(); // runtime init 时初始化的表
ASSERT(set.find(cls) == set.end());
if (!isKnownClass(cls))
set.insert(cls);
if (addMeta)
addClassTableEntry(cls->ISA(), false);
}
可是当我们在rw、ro处进行断点,发现其并没有进入执行,所以rw、ro并不是这里初始化的,不过其内部调用addClassTableEntry初始化类,然后再次调用addClassTableEntry第二个参数为false初始化元类,并将类和元类都加入到runtime init初始化的allocatedClasses表中。readClass的作用是把已经分配过内存空间的类和元类添加到allocatedClasses表中。
我们将初始化非懒加载类代码单独拿出来
// 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]);
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);
}
}
其内部同样调用了addClassTableEntry初始化类,同时还调用了realizeClassWithoutSwift,我们查看其源代码发现,realizeClassWithoutSwift才是真正的初始化本身、父类及元类,处理分类,并进行rw、ro等操作。
static Class realizeClassWithoutSwift(Class cls, Class previously) {
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
……
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
……
// 递归调用 初始化 supercls 和 metacls
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
cls->setInstancesRequireRawIsa();
} else {
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
} else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object")) {
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
} else if (supercls && supercls->getSuperclass() && supercls->instancesRequireRawIsa()) {
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
cls->setSuperclass(supercls); // 设置父类
cls->initClassIsa(metacls); // 设置元类
// 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);
// 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();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) || (supercls && supercls->forbidsAssociatedObjects())) {
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
methodizeClass(cls, previously);
return cls;
}
里面我们也清晰的看到了rw是在该函数中进行的alloc,而ro已经在编译的时候存在了,在这里只是赋值给rw。我们在该源码中加入如下代码然后进行断点调试:
const char *mangledName = cls->nonlazyMangledName();
const char *personName = "WTPerson";
auto wt_ro = (const class_ro_t *)cls->data();
auto isMeta = wt_ro->flags & RO_META;
if (strcmp(mangledName, personName) == 0 && !isMeta) {
}
我们发现,只有在WTPerson中实现了+load方法,才会进入realizeClassWithoutSwift方法,所以这里是初始化非懒加载类的地方。那懒加载类在什么时候初始化的呢,我们在main函数中alloc 一个WTPerson,可以发现其加载流程lookUpImpOrForward -> realizeAndInitializeIfNeeded_locked -> realizeClassMaybeSwiftAndLeaveLocked -> realizeClassMaybeSwiftMaybeRelock -> realizeClassWithoutSwift方法了,所以可以得出懒加载类是在第一次发送消息进行的初始化。
总结
load_images就是调用load方法,所以load方法先于main函数。其流程是会把所有非懒加载类的load方法放到loadable_classes中,把分类的load方法loadable_categories中,然后对其进行遍历,找到相关的类和方法直接使用存储的load method调用。
load的查找流程是父类->子类->分类- 因为
子类中会递归查找父类的load方法,所以我们重写load时是不需要使用[super load]的 - 两个分类都重写了
load,则看编译器顺序,哪个后编译,哪个先执行 - 获取和调用
load方法时都进行了加锁操作,所以load方法是线程安全的
类的真正初始化是由realizeClassWithoutSwift进行的,其内部开辟rw的内存空间,将ro放入rw中,非懒加载类是在main函数之前由map_images统一进行的初始化,懒加载类是在main函数之后使用时进行的初始化。
rebase:将指向错误的内存地址指针修复成指向正确的内存地址,将虚拟内存地址换成真实内存地址