一、前言
在啟動 app 的時候, dyld 會對動態(tài)庫進行加載、鏈接等一系列動作,之后就會來到 libobjc.A.dylib 庫中調用 _objc_init 對類進行處理,通過 map_images 映射出整個鏡像文件,再通過 read_images 加載鏡像文件,此時類已經加載完成,那其中類的加載的流程又是怎么樣的呢?類的屬性、方法、協議都是怎么加載的呢?接下來就從 _objc_init 開始整篇文章的分析。附上 objc 源碼 下載鏈接。
二、_objc_init 源碼分析
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// 環(huán)境變量的一些操作
environ_init();
tls_init();
// 系統(tǒng)級別的 c++ 構造函數調用
static_init();
// 空函數,預留
lock_init();
// 注冊監(jiān)聽異常的回調
exception_init();
// 注冊回調通知
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
-
environ_init 分析
environ_init 方法是讀取影響運行時的環(huán)境變量,最后內部有一段打印環(huán)境變量的代碼。我將它提取出來,不做判斷,打印結果如下。當然也可以在終端使用 export OBJC_HELP=1 指令打印環(huán)境變量。
image.png
我們可以在 Xcode 中修改環(huán)境變量的值來達到我們調試的一些目的。
2.tls_init分析
主要是對線程 key 的綁定。
void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
_objc_pthread_key = TLS_DIRECT_KEY;
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
- static_init 分析
運行 C++ 靜態(tài)構造函數,在 dyld 調用我們自定義構造函數之前。
static void static_init()
{
size_t count;
auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
inits[i]();
}
}
- lock_init 分析
void lock_init(void)
{
}
- exception_init 分析
初始化 libobjc 庫的異常處理系統(tǒng),注冊監(jiān)聽異常崩潰的回調,當發(fā)生崩潰時,就會來到 _objc_terminate 函數里面。
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
static void _objc_terminate(void)
{
if (PrintExceptions) {
_objc_inform("EXCEPTIONS: terminating");
}
if (! __cxa_current_exception_type()) {
// No current exception.
(*old_terminate)();
}
else {
// There is a current exception. Check if it’s an objc exception.
@try {
__cxa_rethrow();
} @catch (id e) {
// It’s an objc object. Call Foundation’s handler, if any.
(*uncaught_handler)((id)e);
(*old_terminate)();
} @catch (...) {
// It’s not an objc object. Continue to C++ terminate.
(*old_terminate)();
}
}
}
三、_dyld_objc_notify_register 源碼分析
整個 objc 在這個里面是一個運行時環(huán)境,運行時環(huán)境去加載所有類的一些信息的時候,就會依賴這個注冊函數的回調的通知,告訴當前的 dyld 的做了哪些事情,你需要哪些環(huán)境來進行彼此的通訊,比如當前的 map_images。
1.知識預備:懶加載類和非懶加載類的區(qū)別
簡單來說就是有沒有實現 load 方法,非懶加載類在類的內部實現了 load 方法,類的加載就會提前,而懶加載類沒有實現 load 方法,在使用的第一次才會加載,當我們再給這個類的發(fā)送消息,如果是第一次,在消息查找的過程中就會判斷這個類是否加載,沒有加載就會加載這個類。
map_images 分析
map_images 方法在 image 加載到內存的時候會觸發(fā)該方法的調用。忽略內部函數跳轉、打印和操作 hCount 的代碼,最終會來到 _read_images。_read_images 分析
(1) _read_images 方法首先創(chuàng)建了兩張表用來存儲類。
// 如果是第一次進來,就會走 if 下面的代碼
if (!doneOnce) {
// 之后就不會來了
// 為什么只來一次呢,因為第一次進來的時候,類,協議,sel,分類都沒有
// 需要創(chuàng)建容器來保存這些東西,這里創(chuàng)建的是兩個表。
doneOnce = YES;
//... 忽略一些無關緊要的代碼
if (DisableTaggedPointers) {
disableTaggedPointers();
}
// TaggedPointer 的優(yōu)化處理
initializeTaggedPointerObfuscator();
// 4/3是 NXMapTable 的負載因子
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
// 實例化存儲類的哈希表,并根據當前類的數量做動態(tài)擴容
// 只要你沒有在共享緩存的類,不管實現或者未實現都會在這個里面
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
// 已經被分配的類和元類都會放在這個表里
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
}
(2) readClass 分析
for (EACH_HEADER) {
// 從編譯后的類列表中取出所有類,獲取到的是一個classref_t類型的指針
classref_t *classlist = _getObjc2ClassList(hi, &count);
if (! mustReadClasses(hi)) {
// 圖像充分優(yōu)化,我們不需要調用 readClass()
continue;
}
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->isPreoptimized();
for (i = 0; i < count; i++) {
// 數組中會取出OS_dispatch_queue_concurrent、OS_xpc_object、NSRunloop等系統(tǒng)類,例如CF、Fundation、libdispatch中的類。以及自己創(chuàng)建的類
Class cls = (Class)classlist[I];
// 通過 readClass 函數獲取處理后的新類,內部主要操作 ro 和 rw 結構體
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;
}
}
}
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->mangledName();
// 如果某個 cls 的 superclass 是 weak-linked 的并且丟失了,則返回YES。
if (missingWeakSuperclass(cls)) {
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
// 添加到重映射表里面,映射為 nil
addRemappedClass(cls, nil);
cls->superclass = nil;
return nil;
}
//... 忽略一些無關緊要的代碼
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
// 對未來的一些類的 ro 和 rw 的特殊處理,一般不會進去
if (Class newCls = popFutureNamedClass(mangledName)) {
// 將objc_class復制到future類的結構中
// 保存future 類的 rw
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro;
memcpy(newCls, cls, sizeof(objc_class));
rw->ro = (class_ro_t *)newCls->data();
newCls->setData(rw);
freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
if (headerIsPreoptimized && !replacing) {
// 斷言
assert(getClassExceptSomeSwift(mangledName));
} else {
// 將 cls 加入到 gdb_objc_realized_classes 表里面去
addNamedClass(cls, mangledName, replacing);
// 將 cls 插入到 allocatedClasses 表里面去
addClassTableEntry(cls);
}
// 供以后參考:共享緩存從不包含mh_bundle
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
// 到此時,這個類在整個表里就有了,返回
return cls;
}
(3) remapClassRef
// 主要是修復重映射 - !noClassesRemapped() 在這里為 false,所以一般走不進來
// 將未映射Class和Super Class重映射,被remap的類都是非懶加載的類
if (!noClassesRemapped()) {
for (EACH_HEADER) {
// 重映射Class,注意是從_getObjc2ClassRefs函數中取出類的引用
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?
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[I]);
}
}
}
(4) sel_registerNameNoLock
// 將所有SEL都注冊到哈希表中,是另外一張哈希表
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->isPreoptimized()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
// sel_cname 將 SEL 強轉為 char 類型
const char *name = sel_cname(sels[i]);
// 注冊 SEL 的操作
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
}
(5) fixupMessageRef
// 修復舊的函數指針調用遺留
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++) {
// 內部將常用的 alloc、objc_msgSend 等函數指針進行注冊,并 fix 為新的函數指針
fixupMessageRef(refs+i);
}
}
(6) readProtocol
// 遍歷所有協議列表,并且將協議列表加載到Protocol的哈希表中
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
// cls = Protocol類,所有協議和對象的結構體都類似,isa都對應Protocol類
Class cls = (Class)&OBJC_CLASS_$_Protocol;
assert(cls);
// 獲取protocol哈希表
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->isPreoptimized();
bool isBundle = hi->isBundle();
// 從編譯器中讀取并初始化Protocol
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
(7) remapProtocolRef
// 修復協議列表引用,優(yōu)化后的 images 可能是正確的,但是并不確定
for (EACH_HEADER) {
// 需要注意到是,下面的函數是 _getObjc2ProtocolRefs,和上面的 _getObjc2ProtocolList 不一樣
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[I]);
}
}
(8) realizeClassWithoutSwift 分析
初始化類就在這一步,首先將非懶加載類從 Mach-O 里面讀取出來,然后通過 realizeClassWithoutSwift 實例化 rw。
// 實現非懶加載的類,對于load方法和靜態(tài)實例變量
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
//... 忽略一些對 cls 的 cache 的一些操作
addClassTableEntry(cls);
//... 忽略無關緊要的代碼
// 實現所有非懶加載的類(實例化類對象的一些信息,例如rw)
realizeClassWithoutSwift(cls);
}
}
// 遍歷 resolvedFutureClasses 數組,實現懶加載的類
// resolvedFutureClasses 數組是在第二步的時候添加懶加載類的
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);
cls->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
static Class realizeClassWithoutSwift(Class cls)
{
runtimeLock.assertLocked();
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
if (!cls) return nil;
// 判斷 cls 是否已經初始化,里面是對 data()->flags 的判斷
if (cls->isRealized()) return cls;
assert(cls == remapClass(cls));
// 驗證類不在共享緩存的未刪除部分
ro = (const class_ro_t *)cls->data();
// 判斷類是否是未實現的未來類
if (ro->flags & RO_FUTURE) {
// 是未來的類. rw 已經被初始化
rw = cls->data();
ro = cls->data()->ro;
// 修改 flags
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// 正常的類. 分配可寫的類數據。
// 開辟 rw 內存空間
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
// 判斷是否是元類
isMeta = ro->flags & RO_META;
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
// 為這個類選擇一個索引
// 設置cls->instancesRequireRawIsa如果沒有更多的索引可用
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// 遞歸調用,實現父類和元類
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
#if SUPPORT_NONPOINTER_ISA
// 禁用一些類和非指針isa
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
// 禁用非指針的 isa
if (DisableNonpointerIsa) {
// 非指針isa禁用的環(huán)境或應用程序SDK版本
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && !(ro->flags & RO_META) &&
0 == strcmp(ro->name, "OS_object"))
{
// 在 hackedDispatch 里 isa 也充當虛表指針
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
// 從元類到根元類設置
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsa(rawIsaIsInherited);
}
// SUPPORT_NONPOINTER_ISA
#endif
// 在重新映射時更新父類和元類
cls->superclass = supercls;
cls->initClassIsa(metacls);
// 協調實例變量的偏移量/布局,可能會重新分配 class_ro_t,更新我們的 ro 變量。
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// 如果還沒有設置就開始設置 fastInstanceSize。
cls->setInstanceSize(ro->instanceSize);
// 將一些標志從 ro 復制到 rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// 從ro或父類中傳播關聯的對象禁止標志
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// 將這個類連接到它的父類的子類列表,即雙向綁定
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// 修復 cls 的方法列表、協議列表和屬性列表,以及附加任何未完成的類別
methodizeClass(cls);
return cls;
}
(8) realizeClassWithoutSwift 分析
初始化類就在這一步,首先將非懶加載類從 Mach-O 里面讀取出來,然后通過 realizeClassWithoutSwift 實例化 rw。
// 實現非懶加載的類,對于load方法和靜態(tài)實例變量
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
//... 忽略一些對 cls 的 cache 的一些操作
addClassTableEntry(cls);
//... 忽略無關緊要的代碼
// 實現所有非懶加載的類(實例化類對象的一些信息,例如rw)
realizeClassWithoutSwift(cls);
}
}
// 遍歷 resolvedFutureClasses 數組,實現懶加載的類
// resolvedFutureClasses 數組是在第二步的時候添加懶加載類的
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);
cls->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
static Class realizeClassWithoutSwift(Class cls)
{
runtimeLock.assertLocked();
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
if (!cls) return nil;
// 判斷 cls 是否已經初始化,里面是對 data()->flags 的判斷
if (cls->isRealized()) return cls;
assert(cls == remapClass(cls));
// 驗證類不在共享緩存的未刪除部分
ro = (const class_ro_t *)cls->data();
// 判斷類是否是未實現的未來類
if (ro->flags & RO_FUTURE) {
// 是未來的類. rw 已經被初始化
rw = cls->data();
ro = cls->data()->ro;
// 修改 flags
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// 正常的類. 分配可寫的類數據。
// 開辟 rw 內存空間
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
// 判斷是否是元類
isMeta = ro->flags & RO_META;
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
// 為這個類選擇一個索引
// 設置cls->instancesRequireRawIsa如果沒有更多的索引可用
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// 遞歸調用,實現父類和元類
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
#if SUPPORT_NONPOINTER_ISA
// 禁用一些類和非指針isa
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
// 禁用非指針的 isa
if (DisableNonpointerIsa) {
// 非指針isa禁用的環(huán)境或應用程序SDK版本
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && !(ro->flags & RO_META) &&
0 == strcmp(ro->name, "OS_object"))
{
// 在 hackedDispatch 里 isa 也充當虛表指針
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
// 從元類到根元類設置
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsa(rawIsaIsInherited);
}
// SUPPORT_NONPOINTER_ISA
#endif
// 在重新映射時更新父類和元類
cls->superclass = supercls;
cls->initClassIsa(metacls);
// 協調實例變量的偏移量/布局,可能會重新分配 class_ro_t,更新我們的 ro 變量。
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// 如果還沒有設置就開始設置 fastInstanceSize。
cls->setInstanceSize(ro->instanceSize);
// 將一些標志從 ro 復制到 rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// 從ro或父類中傳播關聯的對象禁止標志
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// 將這個類連接到它的父類的子類列表,即雙向綁定
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// 修復 cls 的方法列表、協議列表和屬性列表,以及附加任何未完成的類別
methodizeClass(cls);
return cls;
}
methodizeClass 是將 ro 里面的方法、協議以及屬性附加到 rw 里面和把分類中的方法、協議和屬性添加到本類中,這也是分類是添加到本類中的時機,下面單獨講解一下這個方法。
static void methodizeClass(Class cls)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro;
// 將 ro 里面的方法附加到 rw 里面去
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
// 將 ro 里面的屬性附加到 rw 里面去
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
// 將 ro 里面的協議附加到 rw 里面去
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
// 根類獲得額外的方法實現,如果它們還沒有。這些適用于類別替換之前。
if (cls->isRootMetaclass()) {
// SEL SEL_initialize = NULL;
addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
// 返回類的未附加類別列表,并從列表中刪除它們。
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
// 將類別中的方法列表、屬性和協議附加到類中
attachCategories(cls, cats, false /*don’t flush caches*/);
if (cats) free(cats);
// ... 忽略一些無關緊要的代碼
}
其中 attachLists 出現頻率很高,基本上方法、協議、屬性都是通過 attachLists 函數附加到對應的列表上的,接下來單獨介紹一下這個方法。
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
// // 將 addedLists 移動到 array,memmove會對拷貝的數據作檢查,確保內存沒有覆蓋,如果發(fā)現會覆蓋數據,簡單的實現是調轉開始拷貝的位置,從尾部開始拷貝
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
// 將 addedLists 拷貝到 array()->lists,如果復制的兩個區(qū)域存在重疊時使用memcpy,其結果是不可預知的,有可能成功也有可能失敗的
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists,直接追到數組后面
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
(9) addUnattachedCategoryForClass
// 發(fā)現和處理所有Category
for (EACH_HEADER) {
// 外部循環(huán)遍歷找到當前類,查找類對應的Category數組
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
// 內部循環(huán)遍歷當前類的所有Category
category_t *cat = catlist[I];
Class cls = remapClass(cat->cls);
if (!cls) {
// 類別的目標類丟失(可能是弱鏈接)
catlist[i] = nil;
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
// 首先,通過其所屬的類注冊Category。如果這個類已經被實現,則重新構造類的方法列表。
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
// 將Category添加到對應Class的value中,value是Class對應的所有category數組
addUnattachedCategoryForClass(cat, cls, hi);
// 將Category的method、protocol、property添加到Class
// 判斷 cls 是否實現
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
}
// 這塊和上面邏輯一樣,區(qū)別在于這塊是對Meta Class做操作,而上面則是對Class做操作
// 根據下面的邏輯,從代碼的角度來說,是可以對元類添加Category的
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
}
// 初始化從磁盤中加載的所有類,發(fā)現Category必須是最后執(zhí)行的
// 從runtime DebugNonFragileIvars字段一直是 false,所以不會進入這個方法中
if (DebugNonFragileIvars) {
realizeAllClasses();
}
//... 忽略一些打印的代碼
}
(10) 小拓展
在 read_images 里經常出現 _getObjc2ClassRefs 之類的代碼,這是從 Mach-O 文件里面讀取相應 setion 段的數據,我們通過 MachOView 可以看到相關 setion 段的信息。

- load_images 分析
load_images 函數是對 load 方法的加載和調用,接下來我們就來看看底層是怎么對 load 處理的。
void
load_images(const char *path __unused, const struct mach_header *mh)
{
// 如果這里沒有+load方法,則返回時不帶鎖。
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
{
mutex_locker_t lock2(runtimeLock);
// 準備 load 方法
prepare_load_methods((const headerType *)mh);
}
// 調用 load 方法
call_load_methods();
}
進到 prepare_load_methods 可以看到系統(tǒng)是如何加載 load 方法的。
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
// 獲取非懶加載類列表
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
// 循環(huán)遍歷去加載非懶加載類的 load 方法到 loadable_classes
schedule_class_load(remapClass(classlist[i]));
}
// 獲取非懶加載分類列表
category_t **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);
assert(cls->ISA()->isRealized());
// 循環(huán)遍歷去加載非懶加載分類的 load 方法到 loadable_categories
// 和非懶加載類差不多,就是數組不一樣
add_category_to_loadable_list(cat);
}
}
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// 常規(guī)操作,遞歸調用父類加載 load 方法
schedule_class_load(cls->superclass);
// 將 load 方法加載到 loadable_classes
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
// 獲取 load 方法的 imp
method = cls->getLoadMethod();
// 如果沒有 load 方法直接返回
if (!method) return;
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));
}
// loadable_classes 添加 load 方法
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
從上面準備 load 方法的代碼,可以看出,是先去加載父類的,然后再加載本類,之后再去加載分類的 load 方法。我們再看看是怎么調用 load 方法的。
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// 保證只調用一次
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
// do while 循環(huán)調用 load 方法
do {
// 1.重復調用非懶加載類的 load,直到沒有更多的
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2.調用非懶加載分類的 load 方法,和非懶加載類差不多
more_categories = call_category_loads();
// 3. 如果有類或更多未嘗試的類別,則運行更多 load
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
static void call_class_loads(void)
{
int i;
// 取出 loadable_classes
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// 調用保存在 loadable_classes 里的 load 方法
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());
}
// 發(fā)送 load 消息
(*load_method)(cls, SEL_load);
}
// 釋放內存
if (classes) free(classes);
}
總結看 load 的調用,可以得出,load 的調用順序是:父類->本類->分類。
- unmap_image 分析
unmap_image 是用來處理將被 dyld 取消映射的給定 images。
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);
}
void
unmap_image_nolock(const struct mach_header *mh)
{
if (PrintImages) {
_objc_inform("IMAGES: processing 1 newly-unmapped image...\n");
}
header_info *hi;
// 查找映像的運行時 header_info 結構
for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
if (hi->mhdr() == (const headerType *)mh) {
break;
}
}
if (!hi) return;
if (PrintImages) {
_objc_inform("IMAGES: unloading image for %s%s%s\n",
hi->fname(),
hi->mhdr()->filetype == MH_BUNDLE ? " (bundle)" : "",
hi->info()->isReplacement() ? " (replacement)" : "");
}
// 目前只處理 MH_BUNDLE
_unload_image(hi);
// 從標題列表中刪除 header_info
removeHeader(hi);
free(hi);
}
四、 總結
1.類的加載會先來到 _objc_init 函數,執(zhí)行 _dyld_objc_notify_register,再通過 dyld 的 registerObjCNotifiers 回調到 _dyld_objc_notify_register 并執(zhí)行 map_images、load_images、unmap_image。
2.map_images 加載鏡像文件,_read_images 讀取鏡像文件并加載類。
3.第一次調用 _read_images 的時候會去初始化兩張表 gdb_objc_realized_classes 和 allocatedClasses 進行類信息的存儲。
4.初始化表之后就是調用 readClass 是將類插入到 allocatedClasses 表中的。
5.然后再通過 remapClassRef 進行修復類的重映射和 fixupMessageRef 修復舊的函數指針調用遺留。
6.類處理完之后就是添加協議和方法都注冊到哈希表中,方便以后對其進行調用。
7.再通過 realizeClassWithoutSwift 去初始化類,其中是對類的 rw 實例化,以及將 ro 數據賦值到 rw 上。
8.load_images 是對 load 方法的處理以及調用,調用順序是 父類->本類->分類,而有多個分類 load 的時候是根據編譯順序執(zhí)行的。
9.unmap_image 是用來處理將被 dyld 取消映射的給定 images。
