本文以O(shè)C類創(chuàng)建的時(shí)候
alloc
具體都做了什么操作為出發(fā)點(diǎn),去挖掘底層類創(chuàng)建的具體步驟
類創(chuàng)建alloc
時(shí)堆棧操作
首先扣囊,我們創(chuàng)建一個(gè)工程杀怠,新建一個(gè)類,名字隨便起腕唧,我這邊是LMPerson
,下面是具體代碼:
LMPerson *p = [LMPerson alloc];
LMPerson *p1 = [p init];
LMPerson *p2 = [p init];
NSLog(@"%@-%p-%p",p,p,&p);
NSLog(@"%@-%p-%p",p1,p1,&p1);
NSLog(@"%@-%p-%p",p2,p2,&p2);
運(yùn)行程序或辖,查看控制臺(tái)打印信息:
<LMPerson: 0x6000027041e0>-0x6000027041e0-0x7ffee06f3208
<LMPerson: 0x6000027041e0>-0x6000027041e0-0x7ffee06f3200
<LMPerson: 0x6000027041e0>-0x6000027041e0-0x7ffee06f31f8
從打印信息,我們可以看出 類 在alloc
的時(shí)候開辟了一個(gè)內(nèi)存枣接,init
并沒有對內(nèi)存空間做任何操作颂暇, p
,p1
,p2
的內(nèi)存地址相同,指針地址不同但惶。
可以用下面的圖簡單表示:
那alloc
究竟都干了什么呢耳鸯,底層都觸發(fā)哪些方法?這是我們要研究的膀曾,下面是探究源碼的三種方式:
三種探究底層調(diào)用的方式
- 斷點(diǎn)+
control
Step into
斷點(diǎn)+control stepinto1.jpg
斷點(diǎn)+control stepinto2.jpg
- 斷點(diǎn)+
- 符號斷點(diǎn)定位查流程
我們現(xiàn)在知道alloc
會(huì)執(zhí)行objc_alloc
,那我們可以增加符號斷點(diǎn)县爬,看一下下面的流程是什么
符號斷點(diǎn)步驟1.png
符號斷點(diǎn)步驟2.png
- 符號斷點(diǎn)定位查流程
下面就是運(yùn)行項(xiàng)目,此處要注意的是先把objc_alloc
斷點(diǎn)關(guān)閉添谊,程序啟動(dòng)會(huì)加載預(yù)處理的很多類财喳,為了方便我們調(diào)試自己的類alloc
情況,我們走到創(chuàng)建類的alloc
斷點(diǎn)時(shí)斩狱,把objc_alloc
斷點(diǎn)打開耳高,下一步
- Debug匯編查看流程
匯編查看流程調(diào)試1.png
匯編查看流程調(diào)試2.png
下面可以繼續(xù)control
+step into
匯編查看流程調(diào)試3.png
- Debug匯編查看流程
現(xiàn)在我們知道alloc
底層會(huì)調(diào)用objc_alloc
,那怎么看objc_alloc
后面的流程呢所踊,這時(shí)候需要我們?nèi)ゲ榭刺O果的源碼
objc4-818.2.tar.gz泌枪,當(dāng)前是這個(gè)源碼,這個(gè)源碼是不斷更新的秕岛,查看最新的就可以
我們可以編譯這個(gè)源碼去查看底層實(shí)現(xiàn)碌燕,編譯過程有很多問題需要處理,可以從網(wǎng)上找資料解決继薛,也可以從github
上下載別人編譯好的來調(diào)試 修壕,比如 Cooci老師的github
ps:后面自己研究下怎么調(diào)試通源碼
源碼調(diào)試分析--alloc流程
我們把源碼跑起來,一步步根據(jù)alloc
的源碼往里走
這里
__OBJC2__
這個(gè)關(guān)鍵詞就是判斷objc的版本的惋增,我們使用的最新版本叠殷,所以是會(huì)進(jìn)入這里。此處還有兩個(gè)宏定義 :
-
slowpath
:告訴編譯器诈皿,傳入的條件結(jié)果為假的可能性很大 -
fastpath
:告訴編譯器林束,傳入的條件結(jié)果為真的可能性很大
我們一步步step into 會(huì)發(fā)現(xiàn)走到了fastpath
里面像棘,此處有一個(gè)判斷!cls->ISA()->hasCustomAWZ()
1623808971265.jpg
cache里是否有緩存.png
我們看到這里先返回當(dāng)前類的ISA
的Class
對象,判斷cache中有沒有緩存壶冒,有->_objc_rootAllocWithZone
,無->objc_msgSend
缕题,往下執(zhí)行走到了_objc_rootAllocWithZone
_objc_rootAllocWithZone.png
_objc_rootAllocWithZone
調(diào)用了_class_createInstanceFromZone
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
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
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) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
這個(gè)是alloc 內(nèi)的關(guān)鍵方法 ,這里主要有三個(gè)步驟:
-
size = cls->instanceSize(extraBytes);
計(jì)算類需要開辟的內(nèi)存空間大小 -
obj = (id)calloc(1, size);
申請開辟內(nèi)存空間 -
obj->initInstanceIsa(cls, hasCxxDtor);
將類cls和obj指針進(jìn)行關(guān)聯(lián)
到此時(shí)胖腾,alloc
整個(gè)流程執(zhí)行完畢
總結(jié)
alloc
流程圖如下