类加载
类加载的入口是 _objc_init()
函数
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// 读取运行时相关的环境变量
environ_init();
// 绑定线程 key
tls_init();
// 运行系统级别的 C++ 静态构造函数
static_init();
// 创建保存未合并到所属类上的分类的集合,以及已经分配内存的类的集合
runtime_init();
// 初始化 libobjc 的异常处理系统
exception_init();
// 为运行时启动任务注册可 PC 复位的地址范围
cache_init();
// 初始化 libobjc-trampolines.dylib,防止有些程序较早的调用 imp_implementationWithBlock 方法导致崩溃
_imp_implementationWithBlock_init();
// dyld 将 image 加载进内存时会调用 map_images;
// 初始化 image 会调用 load_images;
// 移除 image 会调用 unmap_image
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
在加载时通过 _dyld_objc_notify_register()
函数,以在不同时期分别调用相应函数指针执行指定操作
map_images
// mhCount 是当前 dyld 加载的 Objective-C 的类库的数量
// mhPaths 保存所有加载的 Objective-C 类库的文件地址的数组
// mhdirs 保存所有加载的 Objective-C 类库文件的文件信息数字
*/
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);
}
在 map_images()
函数里只做了加锁保存,剩下的工作都交给了下面 map_images_nolock()
这个函数
map_images_nolock
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;
// 获取进程中 dyld 缓存的起始地址及大小
if (firstTime) {
preopt_init();
}
// 保存镜像中所有的 Objective-C 元类数量
hCount = 0;
// 保存 mhPaths 中的类库中总共包含了多少个 Objective-C 类
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) {
const headerType *mhdr = (const headerType *)mhdrs[i];
// 利用 mhdr 数据,生成一个 header_info 结构体类型的变量 hi,并将 mhdr 保存到该变量中,然后将 hi 添加到链表中保存
// 根据 mhdr 的信息获取该类库里面有多少个 Objective-C 类,添加到 totalClasses 和 totalClasses 中保存
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
if (!hi) {
continue;
}
if (mhdr->filetype == MH_EXECUTE) {
#if __OBJC2__
size_t count;
// 根据 hi 的数据获取类库中方法 SEL 的数量
_getObjc2SelectorRefs(hi, &count);
selrefCount += count;
// 根据 hi 的数据获取类库中消息(IMP + SEL)的数量
_getObjc2MessageRefs(hi, &count);
selrefCount += count;
#else
_getObjcSelectorRefs(hi, &selrefCount);
#endif
}
// 记录 Objective-C 元类数量,并保存 hi 到数组中
hList[hCount++] = hi;
}
if (firstTime) {
// 初始化方法列表并注册内部使用的方法
sel_init(selrefCount);
// 初始化自动释放池
// 初始化保存对象的引用计数的散列表
// 初始化关联对象管理器
arr_init();
}
if (hCount > 0) {
// 加载类的核心逻辑,
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
// 镜像加载完成方法回调
for (auto func : loadImageFuncs) {
for (uint32_t i = 0; i < mhCount; i++) {
func(mhdrs[i]);
}
}
}
在 map_images_nolock()
函数里做的工作是,从镜像中获取类的相关信息、方法的相关信息,初始化一些必要的容器类,并在最后通过方法通知镜像加载完成。但其最核心的工作交给了 _read_images
去做。
_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;
/*
这一部分是对 non-pointer 的判断
如果 Swift 是 3 之前的版本,禁用 non-pointer
如果 OS X 是 10.11 之前的版本,禁用 non-pointer
如果 hi 中有 __DATA 和 __objc_rawisa 数据段,禁用 non-pointer
*/
#if SUPPORT_NONPOINTER_ISA
// Disable non-pointer isa under some conditions.
# if SUPPORT_INDEXED_ISA
// Disable nonpointer isa if any image contains old Swift code
for (EACH_HEADER) {
if (hi->info()->containsSwift() &&
hi->info()->swiftUnstableVersion() < objc_image_info::SwiftVersion3)
{
DisableNonpointerIsa = true;
break;
}
}
# endif
# if TARGET_OS_OSX
// Disable non-pointer isa if the app is too old
// (linked before OS X 10.11)
if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11) {
DisableNonpointerIsa = true;
}
// Disable non-pointer isa if the app has a __DATA,__objc_rawisa section
// New apps that load old extensions may need this.
for (EACH_HEADER) {
if (hi->mhdr()->filetype != MH_EXECUTE) continue;
unsigned long size;
if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) {
DisableNonpointerIsa = true;
}
break; // assume only one MH_EXECUTE image
}
# endif
#endif
// 如果禁用 TaggedPointers,就把相关的掩码置空
if (DisableTaggedPointers) {
disableTaggedPointers();
}
// 用随机的方法初始化 objc_debug_taggedpointer_obfuscator,混淆指针保护代码安全,让用户无法获取支持 TaggedPointers 的类
initializeTaggedPointerObfuscator();
/*
这一部分是创建保存类的表,容量为类数的四分之三
*/
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
}
/*
这一部分是注册所有 SEL 并修正 @selector 的引用
通过 _getObjc2SelectorRefs 函数拿到所有的 SEL,
然后调用 sel_registerNameNoLock 对 SEL 进行注册,
最后通过注册返回 SEL 修正方法名相同的 SEL
*/
// 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;
}
}
}
}
/*
这一部分是获取并保存所有的类
*/
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) {
// 如果该 hi 已经进行了预优化处理就不需要调用 readClass() 函数加载类数据
if (! mustReadClasses(hi, hasDyldRoots)) {
// Image is sufficiently optimized that we need not call 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];
// 加载类的数据,将类添加上面创建的 gdb_objc_realized_classes 表以及 allocatedClasses 表中保存
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;
}
}
}
/*
这一部分是修复需要重映射的类
*/
// 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) {
// 获取 hi 中所有的类引用
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?
// 获取 hi 中所有类的父类引用
classrefs = _getObjc2SuperRefs(hi, &count);
// 操作同上,修复父类引用
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
/*
这一部分是修复老消息的 IMP 指向的函数指针
*/
#if SUPPORT_FIXUP
// Fix up old objc_msgSend_fixup call sites
for (EACH_HEADER) {
// 获取 hi 中的消息
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
// 遍历消息,注册 SEL 并修复部分老的消息 IMP 的指向,如 alloc 消息的 IMP 指向修复为 objc_alloc 等等
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
}
}
#endif
/*
这一部分是获取并修复协议类
*/
bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
// 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();
// 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
// definition
// 如果在首次启动时,发现该协议在支持根目录的共享缓存中已进行了预优化,就跳过加载
if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) {
continue;
}
bool isBundle = hi->isBundle();
// 获取 hi 中的所有协议类并将其保存到上面获取的 protocol_map 表中
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
// Preoptimized images may have the right
// answer already but we don't know for sure.
for (EACH_HEADER) {
// At launch time, we know preoptimized image refs are pointing at the
// shared cache definition of a protocol. We can skip the check on
// launch, but have to visit @protocol refs for shared cache images
// loaded later.
// 依旧在首次启动时跳过在支持根目录的共享缓存中已经预优化协议类的 hi
if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized())
continue;
// 获取 hi 中所有被引用的协议类
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
// 遍历这些被引用的协议类,修复它们的指针,确保指向协议类表中保存的正确地址
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
/*
这一部分是获取分类
*/
// Discover categories.
for (EACH_HEADER) {
// 分类中是否有属性
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
// 处理分类列表的函数(闭包)
auto processCatlist = [&](category_t * const *catlist) {
for (i = 0; i < count; i++) {
// 获取分类
category_t *cat = catlist[i];
// 重映射分类所属的类
Class cls = remapClass(cat->cls);
// 绑定分类及其所属的 hi 结构
locstamped_category_t lc{cat, hi};
if (!cls) {
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)
{
// 针对对象类处理。对象方法、对象协议、对象属性
// 如果该类已实现,直接将分类中的内容添加到类中;否则添加到未附加表中保存
if (cls->isRealized()) {
attachCategories(cls, &lc, 1, ATTACH_EXISTING);
} else {
objc::unattachedCategories.addForClass(lc, cls);
}
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
// 针对元类处理。类方法、类协议、类属性
// 处理方式和对象类相同
if (cls->ISA()->isRealized()) {
attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
} else {
objc::unattachedCategories.addForClass(lc, cls->ISA());
}
}
}
}
};
// 获取 hi 中所有的分类数据调用 processCatlist 函数进行处理
processCatlist(_getObjc2CategoryList(hi, &count));
processCatlist(_getObjc2CategoryList2(hi, &count));
}
/*
这一部分是实现非懒加载的类
非懒加载的类就是实现了 +load 方法的类
*/
// 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()
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
// 获取 hi 中非懒加载的类(实现了 +load 方法的类)
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
// 重映射类,获取正确的类指针
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// 将类及其元类添加到 allocatedClasses 中保存
addClassTableEntry(cls);
// 对类进行初始化
realizeClassWithoutSwift(cls, nil);
}
}
/*
这一部分是实现懒加载的类
懒加载的类就是没有实现了 +load 方法的类
*/
// Realize newly-resolved future classes, in case CF manipulates them
if (resolvedFutureClasses) {
// 遍历懒加载类对其进行初始化,并将该类及其子类全部标记为原始 isa 指针(即 isa 指向类)
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
realizeClassWithoutSwift(cls, nil);
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
// 处理完成后,释放保存懒加载类的数组内存空间
free(resolvedFutureClasses);
}
#undef EACH_HEADER
}
在 _read_images
这个函数里做的事情较多,总结一下就是:
- 判断是否使用 non-pointer 对 isa 进行优化
- 混淆指针保护代码
- 创建保存类的容器
- 获取注册并修正 SEL
- 获取并加载类,把非懒加载的类保存到 3 中创建的容器,为懒加载类创建容器并保存
- 修复需要重映射的类
- 修复需要重映射的老消息的 IMP
- 获取并修复协议类
- 修复需要重映射的协议类引用
- 获取并保存分类,如果分类所属的类已经实现,便将分类的内容添加到类中
- 实现非懒加载的类
- 实现懒加载的类
也就是说在该函数中,从镜像文件中获取了方法、类、协议和分类并进行了相应的处理
realizeClassWithoutSwift
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
if (!cls) return nil;
if (cls->isRealized()) return cls;
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
// 获取类在编译时的数据(ro)
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// 如果是懒加载的类,直接获取已经创建好的运行时的数据(rw)
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
// 修改标识为已实现
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// 如果是普通的类,需要为运行时的数据(rw)申请空间
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
// 将编译时的数据(ro)添加到运行时的数据(rw)中保存
rw->ro = ro;
// 修改标识为已实现
rw->flags = RW_REALIZED|RW_REALIZING;
// 用新创建的运行时的数据(rw)替换类中原编译时的数据(ro)
cls->setData(rw);
}
// 判断当前类是否为元类
isMeta = ro->flags & RO_META;
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// 根据是否为元类设置版本值
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
// 为当前类设置一个索引,如果没有更多的索引可用,就设置类实例对象的 isa 指针为原始指针(即直接指向其所属类)
cls->chooseClassArrayIndex();
// 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.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
// 实现当前类的父类,其中实现其父类必须在设置 RW_REALIZED 之后
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
// 实现当前类的元类,其中实现其元类必须在设置索引之后
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
/*
这一部分主要是判断当前类是否支持 Non-pointer 类型的 isa
*/
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// Metaclasses do not need any features from non pointer ISA
// This allows for a faspath for classes in objc_retain/objc_release.
// 如果是元类,设置它的实例对象的 isa 为原始指针
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) {
// 环境或应用 SDK 版本不支持 Non-pointer isa
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->name, "OS_object"))
{
// 如果是 OS_object 类也不支持 Non-pointer isa
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
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;
}
// 如果该类不支持 Non-pointer isa,那么就将该类及其子类全部标记为不支持 Non-pointer
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
// 重映射该类的父类
cls->superclass = 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
// 同步类和其 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.
// 如果编译时的数据(ro)或者其父类禁止关联对象(AssociatedObject),当前类也应该禁止
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
// 将 ro 及分类中的属性、协议、方法添加到 rw 中
methodizeClass(cls, previously);
return cls;
}
函数 realizeClassWithoutSwift
处理了类的实现逻辑:
- 创建新的包含原 ro 结构的 rw 结构,并替换 ro 在类中的位置
- 实现并关联其重映射后的父类和元类
- 判断当前类是否支持 Non-pointer isa
- 调整类的内存布局
- 将 ro 和分类中的属性、方法、协议添加到 rw 中
load_images
void
load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
// 如果没有需要处理的 +load 方法就到此为止
// 判断没有 +load 方法的依据有两个:镜像中没有非懒加载的类,镜像中没有非懒加载的分类
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
// 获取所有要调用的 +load 方法
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
// 调用所有获取到的 +load 方法
call_load_methods();
}
函数 load_images
中只做了一件事:就是调用类和其分类的 +load
方法。这个过程分为了两步:第一步通过 prepare_load_methods
函数获取数据;第二步调用 call_load_methods
函数处理数据
prepare_load_methods
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
// 获取所有非懒加载类
classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
// 遍历这些类,并将其 +load 方法添加到 loadable_classes 数组中保存,优先添加其父类的 +load 方法
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
// 如果分类所属的类没有实现就先去实现
realizeClassWithoutSwift(cls, nil);
ASSERT(cls->ISA()->isRealized());
// 遍历这些分类,并将其 +load 方法添加到 loadable_categories 数组中保存
add_category_to_loadable_list(cat);
}
}
首先需要获取到所有的类和分类的 +load
方法,prepare_load_methods
函数就是做的这项工作
static void schedule_class_load(Class cls)
{
if (!cls) return;
ASSERT(cls->isRealized()); // _read_images should realize
// 如果该类已经调用过 +load 方法就到此为止
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
// 沿着继承链递归添加其父类的 +load 方法
schedule_class_load(cls->superclass);
// 保存获取到的 +load 方法到数组中等待调用
add_class_to_loadable_list(cls);
// 设置其标志位为已调用过 +load 方法
cls->setInfo(RW_LOADED);
}
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
// 获取类的 +load 方法
method = cls->getLoadMethod();
// 如果类中没有 +load 方法就到此为止
if (!method) return; // Don't bother if cls has no +load method
// 如果 loadable_classes 的容量不足,就按照现有容量的两倍再加 16 进行扩容
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;
// 记录 +load 方法实现的函数指针
loadable_classes[loadable_classes_used].method = method;
// 记录 loadable_classes 数组的使用量
loadable_classes_used++;
}
上面这两个函数就是对类的处理,通过检查当前类及其继承链上的父类的标志位是否已经调用过 +load
方法,如果没调用过,使用结构体 loadable_class
将类及其 +load
方法关联起来,并保存到 loadable_classes
数组中保存等待使用。
这里面有一个小细节:一个是继承链上的父类会被添加到其子类的前面,也就意味着在调用时父类的 +load
方法会先于其子类
void add_category_to_loadable_list(Category cat)
{
IMP method;
loadMethodLock.assertLocked();
// 获取分类的 +load 方法
method = _category_getLoadMethod(cat);
// Don't bother if cat has no +load method
// 如果分类中没有 +load 方法就到此为止
if (!method) return;
// 如果 loadable_categories 的容量不足,就按照现有容量的两倍再加 16 进行扩容
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));
}
// 记录 +load 方法所在的分类
loadable_categories[loadable_categories_used].cat = cat;
// 记录 +load 方法实现的函数指针
loadable_categories[loadable_categories_used].method = method;
// 记录 loadable_categories 数组的使用量
loadable_categories_used++;
}
分类的处理和类很相似,在 add_category_to_loadable_list
函数中,关联分类和其 +load
方法是结构体 loadable_category
,保存数据的是 loadable_categories
数组
call_load_methods
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();
// 遍历并调用 prepare_load_methods 函数中获取到的 +load 方法
do {
// 1. Repeatedly call class +loads until there aren't any more
// 优先遍历 loadable_classes_used(即保存类的 +load 方法的数组)
// 不断调用类的 + load 方法,直到 loadable_classes 为空
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
// 然后遍历 loadable_categories(即保存分类的 +load 方法的数组)
// 只调用一次 call_category_loads 加载分类的 + load 方法
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
// 如果还有更多的类和分类要处理,就继续调用其 +load 方法
} while (loadable_classes_used > 0 || more_categories);
// 销毁自动释放池
objc_autoreleasePoolPop(pool);
// 标识已处理完成
loading = NO;
}
在函数 call_load_methods
中,首先通过静态变量保护了当前函数的处理逻辑,然后创建自动释放池解决在调用 +load
方法时可能出现的内存管理问题,最后开启循环处理获取到的数据。在该函数中可以发现类的 +load
方法是先于分类调用
static void call_class_loads(void)
{
int i;
// Detach current loadable list.
// 创建临时变量保存从 loadable_classes 中获取的数据
struct loadable_class *classes = loadable_classes;
// 创建临时变量保存从 loadable_classes 中获取的总数
int used = loadable_classes_used;
// 置空 loadable_classes 相关变量
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
// 遍历数据
for (i = 0; i < used; i++) {
// 获取 +load 方法所属的类
Class cls = classes[i].cls;
// 获取 +load 方法所实现的函数指针
load_method_t load_method = (load_method_t)classes[i].method;
// 如果没有类就跳过,处理下一个
if (!cls) continue;
// 调用 +load 方法
(*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.
// 创建临时变量保存从 loadable_categories 中获取的数据
struct loadable_category *cats = loadable_categories;
// 创建临时变量保存从 loadable_categories 中获取的总数
int used = loadable_categories_used;
// 创建临时变量保存从 loadable_categories 中获取的容量
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++) {
// 获取 +load 方法所属的分类
Category cat = cats[i].cat;
// 获取 +load 方法所实现的函数指针
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
// 如果没有分类就跳过,处理下一个
if (!cat) continue;
// 获取分类所属的类
cls = _category_getClass(cat);
// cls->isLoadable() 始终为 true,所以只要能拿到分类所属的类就行
if (cls && cls->isLoadable()) {
// 调用 +load 方法
(*load_method)(cls, @selector(load));
// 执行完后将分类指针置空,这一步影响下面的判断
cats[i].cat = nil;
}
}
// Compact detached list (order-preserving)
// 遍历数据,剔除已经调用过 +load 方法的分类,并记录剩下的数量
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);
// 重新申请内存保存未调用过 +load 方法的分类,扩容的策略和之前相同
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.
// 释放原有 loadable_categories 占用的内存
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) {
// 如果还有未调用过 +load 方法的分类
// 将 loadable_categories 指向刚刚创建的 cat 数组
loadable_categories = cats;
// 记录未处理的数量
loadable_categories_used = used;
// 记录新申请的容量
loadable_categories_allocated = allocated;
} else {
// 如果所有分类的 +load 方法都调用过了
// 释放 cats 占用的内存
if (cats) free(cats);
// 置空 loadable_categories 相关变量
loadable_categories = nil;
loadable_categories_used = 0;
loadable_categories_allocated = 0;
}
// 返回是否还有分类需要处理
return new_categories_added;
}
上面这两个函数负责处理类和分类 +load
方法具体调用。逻辑是相似的,遍历数组获取数据,然后调用方法。
unmap_image
void
unmap_image(const char *path __unused, const struct mach_header *mh)
{
recursive_mutex_locker_t lock(loadMethodLock);
mutex_locker_t lock2(runtimeLock);
unmap_image_nolock(mh);
}
unmap_image
函数只是一个入口,其主要工作交给了 unmap_image_nolock
void
unmap_image_nolock(const struct mach_header *mh)
{
header_info *hi;
// Find the runtime's header_info struct for the image
// 查找镜像对应的运行时 header_info 结构体
for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
if (hi->mhdr() == (const headerType *)mh) {
break;
}
}
// 如果没有找到 hi 就到此为止
if (!hi) return;
// 卸载 hi 对应的镜像
_unload_image(hi);
// Remove header_info from header list
// 从链表和数据段中移除 hi
removeHeader(hi);
free(hi);
}
unmap_image_nolock
中做了两件事,一件是卸载 hi 中的数据;另一件就是移除 hi
_unload_image
void _unload_image(header_info *hi)
{
size_t count, i;
loadMethodLock.assertLocked();
runtimeLock.assertLocked();
// Unload unattached categories and categories waiting for +load.
// Ignore __objc_catlist2. We don't support unloading Swift
// and we never will.
// 获取 hi 中的分类
category_t * const *catlist = _getObjc2CategoryList(hi, &count);
// 遍历获取到的分类
for (i = 0; i < count; i++) {
// 获取分类所属的类,如果类不存在就不进行处理
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
// fixme for MH_DYLIB cat's class may have been unloaded already
// unattached list
// 从尚未附加到类中的数组中移除当前分类
objc::unattachedCategories.eraseCategoryForClass(cat, cls);
// +load queue
// 从等待被调用 +load 方法的数组中移除当前分类
remove_category_from_loadable_list(cat);
}
// Unload classes.
// Gather classes from both __DATA,__objc_clslist
// and __DATA,__objc_nlclslist. arclite's hack puts a class in the latter
// only, and we need to unload that class if we unload an arclite image.
objc::DenseSet<Class> classes{};
classref_t const *classlist;
// 获取 hi 中的懒加载类,重定向后添加到 classes 中保存
classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (cls) classes.insert(cls);
}
// 获取 hi 中的非懒加载类,重定向后添加到 classes 中保存
classlist = _getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (cls) classes.insert(cls);
}
// First detach classes from each other. Then free each class.
// This avoid bugs where this loop unloads a subclass before its superclass
for (Class cls: classes) {
// 从等待被调用 +load 方法的数组中移除当前类
remove_class_from_loadable_list(cls);
// 分离当前类的元类
detach_class(cls->ISA(), YES);
// 分离当前类的元类
detach_class(cls, NO);
}
for (Class cls: classes) {
// 释放当前类的元类
free_class(cls->ISA());
// 释放当前类
free_class(cls);
}
// XXX FIXME -- Clean up protocols:
// <rdar://problem/9033191> Support unloading protocols at dylib/image unload time
// fixme DebugUnload
}
卸载 hi 中的数据具体操作就是在 _unload_image
函数中将 hi 中的分类、类、元类及它们关联的数据解除关联并释放掉。其中解除关联是在 detach_class
函数中做的,free_class
则是释放内存
detach_class
static void detach_class(Class cls, bool isMeta)
{
runtimeLock.assertLocked();
// categories not yet attached to this class
// 从尚未附加分类的类的数组中移除当前类
objc::unattachedCategories.eraseClass(cls);
// superclass's subclass list
// 如果当前类已实现
if (cls->isRealized()) {
// 获取当前类的父类
Class supercls = cls->superclass;
if (supercls) {
// 如果当前类有父类,那么将当前类从其父类的子类列表中移除
removeSubclass(supercls, cls);
} else {
// 如果当前类没父类,那么将当前类从根类列表中移除
removeRootClass(cls);
}
}
// class tables and +load queue
if (!isMeta) {
// 如果不是元类
// 从保存所有类的 gdb_objc_realized_classes 表中移除当前类
// 从保存运行时创建的重名类的 nonmeta_class_map 表中移除当前类
removeNamedClass(cls, cls->mangledName());
}
// 从保存已加载类的 allocatedClasses 表中移除当前类
objc::allocatedClasses.get().erase(cls);
}
很明显,detach_class
中就是将类和元类从保存它们的各种容器中移除
free_class
static void free_class(Class cls)
{
runtimeLock.assertLocked();
// 如果当前类没有实现就到此为止
if (! cls->isRealized()) return;
// 获取当前类的读写数据(运行时生成数据)
auto rw = cls->data();
// 获取当前类的只读数据(编译时生成数据)
auto ro = rw->ro;
// 删除当前类的方法缓存数据
cache_delete(cls);
// 释放当前类的方法的 types 指针
for (auto& meth : rw->methods) {
try_free(meth.types);
}
// 释放当前类的方法列表指针
rw->methods.tryFree();
// 从只读数据中获取类的所有成员变量
const ivar_list_t *ivars = ro->ivars;
if (ivars) {
// 分别释放成员变量的偏移量指针、名称指针和类型指针
for (auto& ivar : *ivars) {
try_free(ivar.offset);
try_free(ivar.name);
try_free(ivar.type);
}
// 最后释放当前类的成员变量列表指针
try_free(ivars);
}
// 从读写数据中获取类的所有属性
for (auto& prop : rw->properties) {
// 分别释放属性的名称指针和特性指针
try_free(prop.name);
try_free(prop.attributes);
}
// 释放当前类的属性列表指针
rw->properties.tryFree();
// 释放当前类的协议列表指针
rw->protocols.tryFree();
// 释放当前类的成员变量布局指针
try_free(ro->ivarLayout);
// 释放当前类的弱引用的成员变量布局指针
try_free(ro->weakIvarLayout);
// 释放当前类的名称指针
try_free(ro->name);
// 释放当前类的只读数据指针
try_free(ro);
// 释放当前类的读写数据指针
try_free(rw);
// 释放当前类的指针
try_free(cls);
}
而 free_class
函数中做的就是将类中关联的各种数据结构释放掉,最后释放类自己
removeHeader
void removeHeader(header_info *hi)
{
header_info *prev = NULL;
header_info *current = NULL;
// 遍历保存所有 hi 的链表
for (current = FirstHeader; current != NULL; current = current->getNext()) {
// 找到要移除的 hi
if (current == hi) {
header_info *deadHead = current;
// 从链表中移除
// 如果有前节点,就将前节点的链接指针指向后节点
// 如果没有前节点,就将头指针指向后节点
// 如果要移除的节点是尾节点,就将尾节点置空
// Remove from the linked list.
if (prev)
prev->setNext(current->getNext());
else
FirstHeader = current->getNext(); // no prev so removing head
// Update LastHeader if necessary.
if (LastHeader == deadHead) {
LastHeader = NULL; // will be recomputed next time it's used
}
break;
}
prev = current;
}
// 如果 hi 使用了共享缓存,就从共享缓存的数据段中移除
#if __OBJC2__
if ((hi->mhdr()->flags & MH_DYLIB_IN_CACHE) == 0) {
foreach_data_segment(hi->mhdr(), [](const segmentType *seg, intptr_t slide) {
uintptr_t start = (uintptr_t)seg->vmaddr + slide;
objc::dataSegmentsRanges.remove(start, start + seg->vmsize);
});
}
#endif
}
相比 _unload_image
,removeHeader
中的逻辑相对较少,就是将 hi 从保存它的链表和容器中移除
对象加载
获取实例对象的方法有两种:
[NSObject new];
或
[[NSObject alloc] init];
其中类方法 + (instancetype)new
的实现为:
// Calls [cls new]
id
objc_opt_new(Class cls)
{
#if __OBJC2__
// 检查该类继承链上是否自定义了 new/self/class/respondsToSelector/isKindOfClass 方法,如果没用则调用 callAlloc 函数
if (fastpath(cls && !cls->ISA()->hasCustomCore())) {
return [callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/) init];
}
#endif
// 如果自定义了 new/self/class/respondsToSelector/isKindOfClass 方法,就通过发消息的的方式调用 new 方法
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(new));
}
而类方法 + (instancetype)alloc
的实现为:
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
所有在实例化时 new
等同于 alloc + init
。
alloc
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
// 检查 cls,如果 cls 为 nil 则返回 nil
if (slowpath(checkNil && !cls)) return nil;
// 检查该类继承链上是否自定义了 alloc/allocWithZone: 方法,如果没用则调用 _objc_rootAllocWithZone 函数
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// No shortcuts available.
// allocWithZone 已被置为 false 不可能走里面的方法
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
// 如果自定义了 alloc/allocWithZone: 方法,就通过发消息的的方式调用 alloc 方法
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
在 callAlloc
函数中做的工作是抉择 alloc
的具体实现:是直接调用默认的 _objc_rootAllocWithZone
函数,还是通过发消息的方式调用自定义的 alloc
方法。
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
// 类对象要已经实现
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
// 是否有 C++ 构造函数
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
// 是否有 C++ 析构函数
bool hasCxxDtor = cls->hasCxxDtor();
// 是否是 isa_t 类型的 isa
bool fast = cls->canAllocNonpointer();
size_t size;
// 获取对象在内存中的大小,该值最小为 16 字节
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
// zone 为 nil,不可能调用这个地方了
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
// 申请并初始化堆内存空间
obj = (id)calloc(1, size);
}
// 如果申请内存空间失败
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
if (!zone && fast) {
// 如果是 isa_t 类型的 isa,用这个方法初始化
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
// 如果是 Class 类型的 isa,就用这个方法初始化
obj->initIsa(cls);
}
// 如果没有 C++ 构造函数就直接返回
if (fastpath(!hasCxxCtor)) {
return obj;
}
// 如果有 C++ 构造函数就调用下面的函数进行处理
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
alloc
方法的主要工作是在 _class_createInstanceFromZone
函数中进行的:为对象申请内存空间,并进行一些初始化操作。
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
// 判断当前类的 isa 是否为 isa_t 类型
ASSERT(!cls->instancesRequireRawIsa());
// 判断传入的参数与类中记录的状态是否一致
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
if (!nonpointer) {
// 如果不是 isa_t 类型的,isa 就指向 cls
isa = isa_t((uintptr_t)cls);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
// 创建 isa_t 类型临时变量
isa_t newisa(0);
// 配置 magic 表示当前对象已经创建
// 配置 nonpointer 表示当前对象的 isa 为 isa_t 类型的
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
// 配置 has_cxx_dtor 表示当前对象是否有 C++ 的析构器
newisa.has_cxx_dtor = hasCxxDtor;
// 配置 shiftcls 指向类对象,右移了 3 位是因为类的指针是按照字节(8bits)对齐的,其指针后三位都是没有意义的 0,因此可以右移 3 位进行消除,以减小无意义的内存占用。
newisa.shiftcls = (uintptr_t)cls >> 3;
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
// 将临时变量赋值给结构体成员
isa = newisa;
}
}
初始化操作包括标明 isa
的类型,标识该对象已创建,记录是否有 C++ 的析构器,最后也是最重要的保存指向对象所属类对象的指针。
init
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
init
方法内部其实啥也没做,就是将 alloc
方法创建的对象返回了
__builtin_expect
在 callAlloc
函数中有两个宏,分别是 slowpath
和 fastpath
,他们的具体定义是:
#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))
这两个宏其实使用的是同一个函数 __builtin_expect
,这个函数是 GCC v2.96 版本引入的, 其声明如下:
long __builtin_expect(long exp, long c);
其含义为 exp == c
的概率很大,所以 fastpath
的意思就是 x
很可能为真;slowpath
就是 x
很可能为假