類加載的入口是 _objc_init()
函數swift
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()
函數,以在不一樣時期分別調用相應函數指針執行指定操做數組
// 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()
這個函數緩存
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
去作。安全
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
// 若是禁用 non-pointer,就把相關的掩碼置空
if (DisableTaggedPointers) {
disableTaggedPointers();
}
// 用隨機的方法初始化 objc_debug_taggedpointer_obfuscator,混淆指針保護代碼安全
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
這個函數裏作的事情較多,總結一下就是:bash
也就是說在該函數中,從鏡像文件中獲取了方法、類、協議和分類並進行了相應的處理數據結構
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
處理了類的實現邏輯:閉包
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
函數處理數據app
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
函數就是作的這項工做ide
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
數組
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
方法具體調用。邏輯是類似的,遍歷數組獲取數據,而後調用方法。
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
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
則是釋放內存
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
中就是將類和元類從保存它們的各類容器中移除
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
函數中作的就是將類中關聯的各類數據結構釋放掉,最後釋放類本身
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
。
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++ 的析構器,最後也是最重要的保存指向對象所屬類對象的指針。
- (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
方法建立的對象返回了
在 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
極可能爲假