在上一篇我們分析了_objc_init
方法,程序運行時肴焊,dyld將使用包含objc_image_info
的鏡像文件數(shù)組前联,回調(diào) mapped
函數(shù),最后會執(zhí)行libObjc
的map_images
方法
map_images的主要流程
map_images方法
-
map_images
方法源碼如下娶眷,從注釋可以看出蛀恩,這個方法主要用來處理映射到內(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
方法,里面代碼比較多茂浮,直接看重點
_read_images方法
- 查看
_read_images
方法
_read_images
內(nèi)容比較多双谆,根據(jù)蘋果注釋信息,主要有以下幾步:
- 條件控制進行一次加載
- 修復(fù)預(yù)編譯階段的
@selector
的混亂的問題 - 錯誤混亂的類處理
- 修復(fù)重映射一些沒有被鏡像文件加載進來的類
- 修復(fù)一些消息
- 當類中有協(xié)議時:
readProtocol
- 修復(fù)沒有被加載的協(xié)議
- 分類的處理
- 類的加載處理
- 沒有被處理的類席揽,優(yōu)化那些被侵犯的類
條件控制進行一次加載
doneOnce
是全局靜態(tài)變量顽馋,加載一次后doneOnce=YES
,下次就不會在進入判斷幌羞。第一次進來主要創(chuàng)建表gdb_objc_realized_classes
寸谜,表里存放的是不在dyld共享緩存中的命名類,無論是否實現(xiàn)
static bool doneOnce;
if (!doneOnce) {
doneOnce = YES; // doneOnce:全局靜態(tài)變量属桦,只加載一次
launchTime = YES;
// ...此處省略代碼
// namedClasses 是不在dyld共享緩存中的命名類熊痴,無論是否實現(xiàn)。
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor 4/3是NXMapTable的負載系數(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;
}
}
}
}
錯誤混亂的類處理
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
// 發(fā)現(xiàn)類系谐。修復(fù)未解析的未來類巾陕。標記捆綁類
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ù)未解析的未來類鄙煤。標記捆綁類。: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ā)生混亂茶袒,類運行時可能發(fā)生移動梯刚,但是沒有被刪除,相當于常說的野指針
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;
}
}
}
- 自定義一個類
HTPerson
薪寓,設(shè)置相應(yīng)的斷點
- 執(zhí)行
readClass
方法后亡资,通過lldb查看cls的值澜共,發(fā)現(xiàn)已經(jīng)跟類名關(guān)聯(lián)上了
??我們來探究readClass
方法,看看他做了什么操作
readClass方法
這個方法主要是用來更新兩張表:類名稱表gdb_objc_realized_classes
沟于, 所有類的表allocatedClasses
通過斷點調(diào)試咳胃,我們發(fā)現(xiàn)readClass
主要有三步操作:
- 獲取類名
mangledName
- 將類名和地址關(guān)聯(lián)起來
- 添加【類和元類】到
所有類的表
(allocatedClasses
表)中,就是runtime_init
中開辟的那個表
獲取類名
- 查看
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ù)供璧,因為class_ro_t
結(jié)構(gòu)體中存儲了類名
const class_ro_t *safe_ro() const {
class_rw_t *maybe_rw = data();
if (maybe_rw->flags & RW_REALIZED) {
// maybe_rw is rw
// 已實現(xiàn)的類通過 bits -> rw -> ro來獲取
return maybe_rw->ro();
} else {
// maybe_rw is actually ro
// 未加載過的類bits屬性 存儲的就是 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);
}
}
- 非懶加載類:實現(xiàn)了
+ (void)load
方法的類钠至,程序啟動時會加載 - 懶加載類:沒有實現(xiàn)
+ (void)load
方法的類葛虐,在類首次使用時才會加載 - 可以查看
可執(zhí)行文件
的Mach-O
,在DATA段
的__objc_nlclslist
中存放的是非懶加載類
現(xiàn)在有兩個類:HTPerson
和HTTeacher
棉钧,其中HTPerson
類實現(xiàn)了+ (void)load
方法屿脐,運行程序,通過MachOView
查看可執(zhí)行文件
- 設(shè)置相應(yīng)的斷點宪卿,通過
lldb
打印cls
的诵,發(fā)現(xiàn)此時只有一個非懶加載類,即HTPerson
類佑钾,地址為0x00000001000082e8
西疤,與Mach-O
的非懶加載類表相對應(yīng)
- 接下來我們繼續(xù)分析最重要的一個方法,
realizeClassWithoutSwift
類的加載
realizeClassWithoutSwift方法分析
realizeClassWithoutSwift
對類cls執(zhí)行首次初始化
次绘,包括分配其讀寫數(shù)據(jù)
(即 rw
數(shù)據(jù)瘪阁,用于運行時記錄類的信息),返回類的實際類結(jié)構(gòu)
邮偎。
-
非懶加載類
在程序啟動時
,就會執(zhí)行realizeClassWithoutSwift
方法 -
懶加載類
在使用時才會去加載义黎,我們在方法慢速查找時
有看到過北秽,執(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)實現(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ù),類未初始化時锅减,類結(jié)構(gòu)objc_class的 bits屬性存儲的其實是 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屬性此時存儲的是 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.
// 遞歸實現(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卸夕,編譯器快速計算對象內(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
// 附加類別 - 方法化當前的類,方法排序婆瓜,對類進行擴展
methodizeClass(cls, previously);
return cls;
}
realizeClassWithoutSwift
主要進行了下列幾步操作:
1快集、rw初始化
,這里涉及到干凈內(nèi)存clean memory
和臟內(nèi)存dirty memory
的概念廉白。
-
ro屬于clean memory个初,在編譯時即確定的內(nèi)存空間
,只讀猴蹂,加載后不會發(fā)生改變的內(nèi)存空間院溺,包括類名稱、方法磅轻、協(xié)議和實例變量的信息珍逸; -
rw
的數(shù)據(jù)空間屬于dirty memory
,rw
是運行時的結(jié)構(gòu)聋溜,可讀可寫谆膳,由于其動態(tài)性,可以往類中添加屬性勤婚、方法摹量、協(xié)議。在運行時會發(fā)生變更的內(nèi)存
馒胆。 -
rwe
類的額外信息缨称。在WWDC2020
中也提到,只有不到10%
的類真正的更改了他們的方法祝迂,并不是每一個類都需要插入數(shù)據(jù)睦尽,進行修改的類很少,避免資源的消耗型雳,所以就有了rwe
当凡。
2、遞歸處理纠俭,進行父類和元類的實現(xiàn)沿量。
3、isa處理
冤荆,在前面學(xué)習(xí)isa
的時候朴则,對于NONPOINTER_ISA
進行了位域處理,指針優(yōu)化钓简,isa
的末尾位是1
乌妒,isa
不單單代表一個指針
汹想。而對于元類以及特殊情況下的場景的一些類,無需開啟指針優(yōu)化的類撤蚊,使用Raw Isa
古掏,isa
的末尾位是0。
4侦啸、設(shè)置superclass
和 isa
屬性槽唾,用來獲取父類和元類
5、設(shè)置fastInstanceSize
匹中,編譯器快速計算對象內(nèi)存大小
6夏漱、c++析構(gòu)函數(shù)
的相關(guān)設(shè)置,以及關(guān)聯(lián)對象
的相關(guān)設(shè)置顶捷。
7挂绰、建立子類與父類的雙定鏈表關(guān)系
,保證子類能找到父類服赎,父類也可以找到子類葵蒂。
8、方法化當前的類重虑,向類中添加方法践付,協(xié)議、屬性缺厉,同時對方法列表進行排序等操作永高。
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é)議進行處理。見下圖:
從上圖可以發(fā)現(xiàn):rwe為空
辐脖,很顯然饲宛,此時還沒有對類進行相關(guān)的擴展操作,所以rwe
還沒有被創(chuàng)建初始化嗜价。此時針對方法艇抠、屬性、協(xié)議的添加操作時無效的
久锥! 2家淤、方法列表的處理中有些不同,調(diào)用了
prepareMethodLists
方法瑟由。那么該方法做了哪些操作呢媒鼓?見下圖:
核心流程,fixupMethodList
错妖,根據(jù)注釋:根據(jù)需要對selector
進行修復(fù)绿鸣。進入fixupMethodList方法
,查看實現(xiàn)流程暂氯。見下圖:
- 繼續(xù)
methodizeClass
源碼的解讀潮模。找到了類初始化過程中非常關(guān)鍵的步驟,向類中添加分類方法痴施、協(xié)議等擎厢,rwe
的初始化也在其中。