知識(shí)點(diǎn)
1:
dirty memory
: 臟內(nèi)存, 支持增刪改的內(nèi)存區(qū)域
eg:rw
結(jié)構(gòu)體
2:clean memory
: 干凈內(nèi)存, 只支持讀的內(nèi)存區(qū)域
eg:ro
結(jié)構(gòu)體
為什么這么設(shè)計(jì)呢?
蘋(píng)果的內(nèi)存優(yōu)化操作, 防止
干凈內(nèi)存
(不經(jīng)常修改的內(nèi)存區(qū)域)受到污染, 比如方法列表, 如果蘋(píng)果不區(qū)分兩塊兒區(qū)域的話(huà), 意味著每次運(yùn)行時(shí)動(dòng)態(tài)添加的方法和分類(lèi)里擴(kuò)展的方法都會(huì)被寫(xiě)入進(jìn)去, 那么就會(huì)導(dǎo)致objc_class
整片區(qū)域的內(nèi)存都要受到污染, 導(dǎo)致內(nèi)存增大, 性能耗損
. 但是區(qū)分開(kāi)后, 我們只處理dirty memory
中的methods
列表就會(huì)是內(nèi)存和性能得到很大的提升
詳見(jiàn): Runtime2020升級(jí)改版-更小, 更安全, 更高效
1: 慢速查詢(xún)的進(jìn)入方式
方法1: 匯編最終流程發(fā)現(xiàn)
這里跟
lookUpImpOrForward
方法實(shí)現(xiàn)中的這塊代碼相對(duì)應(yīng)
// 這里是預(yù)防多線(xiàn)程有可能調(diào)用緩存方法的情況
// 需要注意behavior & LOOKUP_CACHE的結(jié)果
// behavior: cache查找完后會(huì)傳3, &后為0, 會(huì)跳過(guò)cache快速查找
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
方法2: 匯編
通過(guò)注釋掉實(shí)現(xiàn), 通過(guò)
debug Workflow
->always show disassembly
查看匯編流程,objc_msgSend
->(ctrl + step info)_objc_msgSend_uncached
->lookUpImpOrForward
2: 解析lookUpImpOrForward
方法
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
runtimeLock.assertUnlocked();
// 1.
// 這里是預(yù)防多線(xiàn)程有可能調(diào)用緩存方法的情況
// 需要注意behavior & LOOKUP_CACHE的結(jié)果
// behavior: cache查找完后會(huì)傳3, &后為0, 跳過(guò)cache快速查找
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
// 加鎖(保障線(xiàn)程安全)
runtimeLock.lock();
// 2. 是否是已注冊(cè)驗(yàn)證的Class, 防止代碼被惡意入侵, 注入
checkIsKnownClass(cls);
// 3. 檢查是否進(jìn)行了類(lèi)初始化
if (slowpath(!cls->isRealized())) {
// 4. 內(nèi)部進(jìn)行了ro和rw等基礎(chǔ)信息的分配, 以及cls作為一個(gè)雙向鏈表的繼承關(guān)系綁定操作
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
}
// 5. 檢查是否完成類(lèi)信息的初始化
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
// 6. 內(nèi)部將遞歸初始化所有類(lèi)并自動(dòng)發(fā)送initialize方法
// 根據(jù)需要將“ + initialize”消息發(fā)送給任何繼承鏈上未初始化的類(lèi)
// ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
runtimeLock.assertLocked();
curClass = cls;
// 7. 這里的for是死循環(huán), 因?yàn)闆](méi)有設(shè)置循環(huán)結(jié)束條件
for (unsigned attempts = unreasonableClassCount();;) {
// 1. 先查找自己的method list里有沒(méi)有, 而不查找父類(lèi)的
// 內(nèi)部查找方法findMethodInSortedMethodList用到了二分查找算法, 提高查找效率, 詳見(jiàn)注釋
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {//查到了則跳出循環(huán)完成
imp = meth->imp;
goto done;
}
// 2. curClass在這里會(huì)遞歸賦值為它的父類(lèi),直到繼承關(guān)系的最終點(diǎn)nil時(shí), 則會(huì)賦值imp為forward_imp
if (slowpath((curClass = curClass->superclass) == nil)) {
imp = forward_imp;
// 3. 這里是for死循環(huán)的出口條件
break;
}
// Halt if there is a cycle in the superclass chain.
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
// 4. Superclass cache.
// 因?yàn)樯厦鎐urClass會(huì)遞歸被賦值為它的父類(lèi), 所以這里查找到的是父類(lèi)的緩存 -> 匯編快速查找cache
// 注意: cache_getimp 快速查詢(xún)時(shí), 傳入?yún)?shù)是`GETIMP`
// 如果沒(méi)找到, 最終jumpMiss返回的是`LGetImpMiss`, 即0x0
imp = cache_getImp(curClass, sel);
// 5. 當(dāng)在父類(lèi)找到的imp等于forward_imp時(shí), 跳出循環(huán)
if (slowpath(imp == forward_imp)) {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
// 6. 如果cache_getImp找到了imp在這跳出循環(huán)完成
if (fastpath(imp)) {
// Found the method in a superclass. Cache it in this class.
goto done;
}
}
// 8. No implementation found. Try method resolver once.
// 動(dòng)態(tài)方法決議,
// behavior & LOOKUP_RESOLVER)類(lèi)似上面, 作用是只調(diào)用一次
// 待驗(yàn)證: 通過(guò)這里實(shí)現(xiàn)消息轉(zhuǎn)發(fā)的, 這里會(huì)只調(diào)用1次
// 沒(méi)有實(shí)現(xiàn)消息轉(zhuǎn)發(fā)的, 則會(huì)調(diào)用2次
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
// 9. 動(dòng)態(tài)方法決議
return resolveMethod_locked(inst, sel, cls, behavior);
}
done:
// 10. 0將查找到的方法寫(xiě)入緩存, 避免下次調(diào)用還是慢速查找, 并形成一個(gè)閉環(huán)
log_and_fill_cache(cls, imp, sel, inst, curClass);
runtimeLock.unlock();
done_nolock:
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
return imp;
}
1)重要流程2-4. cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
- 下面這個(gè)方法的實(shí)現(xiàn), 是在慢速查找流程前的重點(diǎn), 它準(zhǔn)備了class信息中
ro和rw
等基礎(chǔ)信息, 以及雙向鏈表
(父類(lèi), 元類(lèi), 子類(lèi))的處理, 確認(rèn)傳入對(duì)象的繼承關(guān)系
, 給后面遞歸其父類(lèi)緩存方法
的流程提供準(zhǔn)備工作
static Class realizeClassWithoutSwift(Class cls, Class previously)
??
// 雙向鏈表, 確認(rèn)繼承關(guān)系
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
??
cls->superclass = supercls;
cls->initClassIsa(metacls);
??
// 將此類(lèi)連接到其超類(lèi)的子類(lèi)列表
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
2)核心流程2-7-1 Method meth = getMethodNoSuper_nolock(curClass, sel);
查找主類(lèi)或其分類(lèi)的方法列表
: 采用二分查找
算法, 提高查詢(xún)效率, 減少性能損耗
Method meth = getMethodNoSuper_nolock(curClass, sel);
??
ALWAYS_INLINE static method_t *
findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
ASSERT(list);
// list: 是個(gè)排完序, 并從小到大遞增的方法列表.eg: 0, 1, 2,
// &list->first: 取址, 并找到首元素
const method_t * const first = &list->first;
const method_t *base = first;
const method_t *probe;
// 要找的SEL, SEL被強(qiáng)轉(zhuǎn)為uintptr_t, 用來(lái)比較大小
uintptr_t keyValue = (uintptr_t)key;
uint32_t count;
// 1: 二分查找
//count >>= 1 等價(jià)于count/2 16進(jìn)制
for (count = list->count; count != 0; count >>= 1) {
//內(nèi)存偏移(base指針地址偏移(count >> 1)個(gè)位置)
probe = base + (count >> 1);
uintptr_t probeValue = (uintptr_t)probe->name;
if (keyValue == probeValue) {
// 這個(gè)當(dāng)sel相同時(shí), 需要循環(huán)遞減查找到它的分類(lèi)的方法實(shí)現(xiàn), 因?yàn)榧虞d到內(nèi)存時(shí), 分類(lèi)會(huì)加載在主類(lèi)的前邊
// 也可以側(cè)面驗(yàn)證分類(lèi)可以重寫(xiě)并覆蓋主類(lèi)的方法的調(diào)用
while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
probe--;
}
return (method_t *)probe;
}
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
/* * 舉個(gè)栗子
**運(yùn)行流程舉例1**: 編譯斷點(diǎn)走一走, 你懂我也懂
list = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 ,0x8];
base = 0x1
keyValue = 0x7;
運(yùn)行流程舉例1: 編譯斷點(diǎn)走一走, 你懂我也懂
1: count: 8; count >> 1: 4; probe = 0x5(0x1+4);
1): keyValue > probeValue: true;
base = 0x6 (probe + 1); count: 7(count--)
2): 進(jìn)入下一次循環(huán)
2: count: 7(--); count >>= 1: 3; probe = 0x6 + 1(3>>1): 0x7;
命中返回.
-----------------------------------------------------------------
**運(yùn)行流程舉例2**: 編譯斷點(diǎn)走一走, 你懂我也懂
list = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 ,0x8];
base = 0x1
keyValue = 0x2;
1: count: 8; probe = 0x5(0x1+4(count>>1));
1): keyValue > probeValue: false;
2): 進(jìn)入下一次循環(huán): true
base = 0x1; count: 8 (都不變)
2: count: 8; count >>= 1: 4; probe = 0x1 + 2(4>>1): 0x3;
1): keyValue > probeValue: false;
2): 進(jìn)入下一次循環(huán): true
base = 0x1; count: 4 (都不變)
3: count: 4; count >>= 1: 2; probe = 0x1 + 1(4>>1): 0x2;
命中返回
**/
return nil;
}
3)核心流程2-8和9. resolveMethod_locked(inst, sel, cls, behavior);
// 動(dòng)態(tài)方法決議,
// behavior & LOOKUP_RESOLVER)類(lèi)似上面, 作用是只調(diào)用一次
// 待驗(yàn)證: 通過(guò)這里實(shí)現(xiàn)消息轉(zhuǎn)發(fā)的, 這里會(huì)只調(diào)用1次
// 沒(méi)有實(shí)現(xiàn)消息轉(zhuǎn)發(fā)的, 則會(huì)調(diào)用2次
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
// 動(dòng)態(tài)方法決議
return resolveMethod_locked(inst, sel, cls, behavior);
}