在上一篇我們分析了_objc_init方法,程序運(yùn)行時(shí),dyld將使用包含objc_image_info的鏡像文件數(shù)組,回調(diào) mapped 函數(shù),最后會(huì)執(zhí)行libObjc的map_images方法
map_images的主要流程
map_images方法
-
map_images方法源碼如下,從注釋可以看出,這個(gè)方法主要用來處理映射到內(nèi)存中的鏡像文件
/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
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_nolock方法
- 查看
map_images_nolock方法,里面代碼比較多,直接看重點(diǎn)

_read_images方法
- 查看
_read_images方法

_read_images內(nèi)容比較多,根據(jù)蘋果注釋信息,主要有以下幾步:
- 條件控制進(jìn)行一次加載
- 修復(fù)預(yù)編譯階段的
@selector的混亂的問題 - 錯(cuò)誤混亂的類處理
- 修復(fù)重映射一些沒有被鏡像文件加載進(jìn)來的類
- 修復(fù)一些消息
- 當(dāng)類中有協(xié)議時(shí):
readProtocol - 修復(fù)沒有被加載的協(xié)議
- 分類的處理
- 類的加載處理
- 沒有被處理的類,優(yōu)化那些被侵犯的類
條件控制進(jìn)行一次加載
doneOnce是全局靜態(tài)變量,加載一次后doneOnce=YES,下次就不會(huì)在進(jìn)入判斷。第一次進(jìn)來主要?jiǎng)?chuàng)建表gdb_objc_realized_classes,表里存放的是不在dyld共享緩存中的命名類,無論是否實(shí)現(xiàn)
static bool doneOnce;
if (!doneOnce) {
doneOnce = YES; // doneOnce:全局靜態(tài)變量,只加載一次
launchTime = YES;
// ...此處省略代碼
// namedClasses 是不在dyld共享緩存中的命名類,無論是否實(shí)現(xiàn)。
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor 4/3是NXMapTable的負(fù)載系數(shù)
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
// 創(chuàng)建哈希表 存放所有的類
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
ts.log("IMAGE TIMES: first time tasks");
}
修復(fù)@selector的混亂
- 修復(fù)@selector的混亂,從macho文件中獲取對象方法列表,方法列表存放在
DATA段的__objc_selrefs
// Fix up @selector references
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->hasPreoptimizedSelectors()) continue;
bool isBundle = hi->isBundle();
// 從macho文件中獲取方法名列表,方法列表存放在 DATA段 的 __objc_selrefs
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) {
// TODO:自定義打印
_objc_inform("=======修復(fù)@selector的混亂:%p = %p", (SEL)sels[i], sel);
sels[i] = sel;
}
}
}
}

錯(cuò)誤混亂的類處理
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
// 發(fā)現(xiàn)類。修復(fù)未解析的未來類。標(biāo)記捆綁類
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) {
if (! mustReadClasses(hi, hasDyldRoots)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
// 從Mach-O中讀取類列表信息,類總數(shù)count
classref_t const *classlist = _getObjc2ClassList(hi, &count);
if (hIndex == hCount - 1) {
_objc_inform("=======發(fā)現(xiàn)類。修復(fù)未解析的未來類。標(biāo)記捆綁類。:HTTest = %zu", count);
}
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[I];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
// 類信息發(fā)生混亂,類運(yùn)行時(shí)可能發(fā)生移動(dòng),但是沒有被刪除,相當(dāng)于常說的野指針
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;
}
}
}
- 自定義一個(gè)類
HTPerson,設(shè)置相應(yīng)的斷點(diǎn)

- 執(zhí)行
readClass方法后,通過lldb查看cls的值,發(fā)現(xiàn)已經(jīng)跟類名關(guān)聯(lián)上了

??我們來探究readClass方法,看看他做了什么操作
readClass方法
這個(gè)方法主要是用來更新兩張表:類名稱表gdb_objc_realized_classes, 所有類的表allocatedClasses

通過斷點(diǎn)調(diào)試,我們發(fā)現(xiàn)readClass主要有三步操作:
- 獲取類名
mangledName - 將類名和地址關(guān)聯(lián)起來
- 添加【類和元類】到
所有類的表(allocatedClasses表)中,就是runtime_init中開辟的那個(gè)表
獲取類名
- 查看
nonlazyMangledName方法,內(nèi)部是通過bites屬性找到ro數(shù)據(jù),類名存放在class_ro_t結(jié)構(gòu)中
// Get the class's mangled name, or NULL if the class has a lazy
// name that hasn't been created yet.
const char *nonlazyMangledName() const {
return bits.safe_ro()->getName();
}
- 查看
safe_ro方法,內(nèi)部就是獲取ro結(jié)構(gòu)體數(shù)據(jù),因?yàn)?code>class_ro_t結(jié)構(gòu)體中存儲(chǔ)了類名
const class_ro_t *safe_ro() const {
class_rw_t *maybe_rw = data();
if (maybe_rw->flags & RW_REALIZED) {
// maybe_rw is rw
// 已實(shí)現(xiàn)的類通過 bits -> rw -> ro來獲取
return maybe_rw->ro();
} else {
// maybe_rw is actually ro
// 未加載過的類bits屬性 存儲(chǔ)的就是 ro結(jié)構(gòu)體數(shù)據(jù)
return (class_ro_t *)maybe_rw;
}
}


addNamedClass將類名和地址關(guān)聯(lián)綁定起來
- 查看
addNamedClass方法,查看類名和地址是如何關(guān)聯(lián)起來
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
inform_duplicate(name, old, cls);
// getMaybeUnrealizedNonMetaClass uses name lookups.
// Classes not found by name lookup must be in the
// secondary meta->nonmeta table.
addNonMetaClass(cls);
} else {
// 更新gdb_objc_realized_classes表,將key設(shè)置為 name value 設(shè)置為cls
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
ASSERT(!(cls->data()->flags & RO_META));
// wrong: constructed classes are already realized when they get here
// ASSERT(!cls->isRealized());
}
- 更新
gdb_objc_realized_classes哈希表,key是name,value是cls
添加【類和元類】到 所有類的表中
- 查看
addClassTableEntry方法,源碼如下,這一步就是將類和元類加入到allocatedClasses表中
static void
addClassTableEntry(Class cls, bool addMeta = true)
{
runtimeLock.assertLocked();
// This class is allowed to be a known class via the shared cache or via
// data segments, but it is not allowed to be in the dynamic table already.
// _objc_init -> runtime_init 中初始化的表:所有類的表
auto &set = objc::allocatedClasses.get();
ASSERT(set.find(cls) == set.end());
if (!isKnownClass(cls))
set.insert(cls);
if (addMeta)
// 將元類插入哈希表中
addClassTableEntry(cls->ISA(), false);
}
非懶加載類的加載
// +load方法的調(diào)用,是在 load_images方法中
// +load handled by prepare_load_methods()
// 加載非懶加載類
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
// 從Mach-O中讀取非懶加載類列表信息,非懶加載類總數(shù)count
classref_t const *classlist = hi->nlclslist(&count);
if (hIndex == hCount - 1) {
_objc_inform("=======加載非懶加載類:HTTest = %zu", count);
}
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// 添加【非懶加載類和他的元類】到 所有類的表中
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
realizeClassWithoutSwift(cls, nil);
}
}
- 非懶加載類:實(shí)現(xiàn)了
+ (void)load方法的類,程序啟動(dòng)時(shí)會(huì)加載 - 懶加載類:沒有實(shí)現(xiàn)
+ (void)load方法的類,在類首次使用時(shí)才會(huì)加載 - 可以查看
可執(zhí)行文件的Mach-O,在DATA段的__objc_nlclslist中存放的是非懶加載類
現(xiàn)在有兩個(gè)類:HTPerson和HTTeacher,其中HTPerson類實(shí)現(xiàn)了+ (void)load方法,運(yùn)行程序,通過MachOView查看可執(zhí)行文件


- 設(shè)置相應(yīng)的斷點(diǎn),通過
lldb打印cls,發(fā)現(xiàn)此時(shí)只有一個(gè)非懶加載類,即HTPerson類,地址為0x00000001000082e8,與Mach-O的非懶加載類表相對應(yīng)

- 接下來我們繼續(xù)分析最重要的一個(gè)方法,
realizeClassWithoutSwift
image
類的加載
realizeClassWithoutSwift方法分析
realizeClassWithoutSwift對類cls執(zhí)行首次初始化,包括分配其讀寫數(shù)據(jù)(即 rw數(shù)據(jù),用于運(yùn)行時(shí)記錄類的信息),返回類的實(shí)際類結(jié)構(gòu)。
-
非懶加載類在程序啟動(dòng)時(shí),就會(huì)執(zhí)行realizeClassWithoutSwift方法 -
懶加載類在使用時(shí)才會(huì)去加載,我們在方法慢速查找時(shí)有看到過,執(zhí)行流程:lookUpImpOrForward-->realizeAndInitializeIfNeeded_locked-->realizeClassMaybeSwiftAndLeaveLocked-->realizeClassMaybeSwiftMaybeRelock-->realizeClassWithoutSwift

-
realizeClassWithoutSwift方法源碼如下,代碼比較多,源碼如下
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
// 如果類已經(jīng)實(shí)現(xiàn),直接返回
if (cls->isRealized()) {
validateAlreadyRealizedClass(cls);
return cls;
}
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
// 獲取ro數(shù)據(jù),類未初始化時(shí),類結(jié)構(gòu)objc_class的 bits屬性存儲(chǔ)的其實(shí)是 ro數(shù)據(jù)
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
// 開辟可讀寫數(shù)據(jù),即rw,bits屬性此時(shí)存儲(chǔ)的是 rw數(shù)據(jù)
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
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)" : "");
}
// 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.
// 遞歸實(shí)現(xiàn) 父類和元類
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#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.
cls->setInstancesRequireRawIsa();
} else {
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->getSuperclass() &&
supercls->instancesRequireRawIsa())
{
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
// 設(shè)置superclass 和 isa
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// Set fastInstanceSize if it wasn't set already.
// 設(shè)置fastInstanceSize,編譯器快速計(jì)算對象內(nèi)存大小
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// Connect this class to its superclass's subclass lists
// 建立類 子類的雙向鏈表關(guān)系
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
// 附加類別 - 方法化當(dāng)前的類,方法排序,對類進(jìn)行擴(kuò)展
methodizeClass(cls, previously);
return cls;
}
realizeClassWithoutSwift主要進(jìn)行了下列幾步操作:
1、rw初始化,這里涉及到干凈內(nèi)存clean memory和臟內(nèi)存dirty memory的概念。
-
ro屬于clean memory,在編譯時(shí)即確定的內(nèi)存空間,只讀,加載后不會(huì)發(fā)生改變的內(nèi)存空間,包括類名稱、方法、協(xié)議和實(shí)例變量的信息; -
rw的數(shù)據(jù)空間屬于dirty memory,rw是運(yùn)行時(shí)的結(jié)構(gòu),可讀可寫,由于其動(dòng)態(tài)性,可以往類中添加屬性、方法、協(xié)議。在運(yùn)行時(shí)會(huì)發(fā)生變更的內(nèi)存。 -
rwe類的額外信息。在WWDC2020中也提到,只有不到10%的類真正的更改了他們的方法,并不是每一個(gè)類都需要插入數(shù)據(jù),進(jìn)行修改的類很少,避免資源的消耗,所以就有了rwe。


2、遞歸處理,進(jìn)行父類和元類的實(shí)現(xiàn)。

3、isa處理,在前面學(xué)習(xí)isa的時(shí)候,對于NONPOINTER_ISA進(jìn)行了位域處理,指針優(yōu)化,isa的末尾位是1,isa不單單代表一個(gè)指針。而對于元類以及特殊情況下的場景的一些類,無需開啟指針優(yōu)化的類,使用Raw Isa,isa的末尾位是0。

4、設(shè)置superclass 和 isa屬性,用來獲取父類和元類

5、設(shè)置fastInstanceSize,編譯器快速計(jì)算對象內(nèi)存大小

6、c++析構(gòu)函數(shù)的相關(guān)設(shè)置,以及關(guān)聯(lián)對象的相關(guān)設(shè)置。

7、建立子類與父類的雙定鏈表關(guān)系,保證子類能找到父類,父類也可以找到子類。

8、方法化當(dāng)前的類,向類中添加方法,協(xié)議、屬性,同時(shí)對方法列表進(jìn)行排序等操作。

methodizeClass方法分析
methodizeClass方法源碼如下:
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
if (rwe) rwe->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
// Root classes get bonus method implementations if they don't have
// them already. These apply before category replacements.
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
// When a class relocates, categories with class methods
// may be registered on the class itself rather than on
// the metaclass. Tell attachToClass to look for those.
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
#if DEBUG
// Debug: sanity-check all SELs; log method list contents
for (const auto& meth : rw->methods()) {
if (PrintConnecting) {
_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
cls->nameForLogging(), sel_getName(meth.name()));
}
ASSERT(sel_registerName(sel_getName(meth.name())) == meth.name());
}
#endif
}
-
1、對方法、屬性、協(xié)議進(jìn)行處理。見下圖:
image
從上圖可以發(fā)現(xiàn):rwe為空,很顯然,此時(shí)還沒有對類進(jìn)行相關(guān)的擴(kuò)展操作,所以rwe還沒有被創(chuàng)建初始化。此時(shí)針對方法、屬性、協(xié)議的添加操作時(shí)無效的! 2、方法列表的處理中有些不同,調(diào)用了
prepareMethodLists方法。那么該方法做了哪些操作呢?見下圖:

核心流程,fixupMethodList,根據(jù)注釋:根據(jù)需要對selector進(jìn)行修復(fù)。進(jìn)入fixupMethodList方法,查看實(shí)現(xiàn)流程。見下圖:

- 繼續(xù)
methodizeClass源碼的解讀。找到了類初始化過程中非常關(guān)鍵的步驟,向類中添加分類方法、協(xié)議等,rwe的初始化也在其中。


