一、調(diào)試方法:
開篇我們先來介紹三種可以進(jìn)入libobjc.A.dylib(objc_alloc所在的動態(tài)庫)
的調(diào)試方法,需要用真機(jī)進(jìn)行調(diào)試:
1、直接下斷點(diǎn):斷點(diǎn)在Person *p1 = [Person alloc];
行 -> 按住control -> 點(diǎn)擊 step into進(jìn)入;
2竹椒、符號斷點(diǎn):
3、通過匯編查看:斷點(diǎn)在Person *p1 = [Person alloc];
行匈挖,開啟Always Show Disassembly
,如圖:
二碾牌、接下來我們看一段代碼:
Person *p1 = [Person alloc];
Person *p2 = [p1 init];
Person *p3 = [p1 init];
NSLog(@"?%@ - %p",p1,&p1);
NSLog(@"?%@ - %p",p2,&p2);
NSLog(@"?%@ - %p",p3,&p3);
打印結(jié)果如下:
?<Person: 0x600003885d40> - 0x7ffeec2e10c8
?<Person: 0x600003885d40> - 0x7ffeec2e10c0
?<Person: 0x600003885d40> - 0x7ffeec2e10b8
以上我們可以看出p1康愤、p2、p3三個不同的指針指向的是同一片內(nèi)存空間,init
大概貌似啥也沒干舶吗。
三征冷、下面我們詳細(xì)看一下初始化對象時常用的alloc/init/new都做了些啥?
準(zhǔn)備工作:源碼下載及配置
1誓琼、 alloc
探索
1) 流程圖:
2) 理解:
-
alloc
創(chuàng)建對象并申請內(nèi)存空間检激,也伴隨著給當(dāng)前對象賦予了指針地址; - 我們看到
alloc
后直接走了rootAlloc
,而不是objc_alloc
,這一部分其實是編譯器幫我們優(yōu)化了腹侣,可以通過llvm源碼
來驗證叔收。 -
objc_alloc
只走一次,同樣可以通過llvm源碼
來驗證傲隶。 -
alloc
是否具有創(chuàng)建對象的能力:流程返回的時候x0
是否會存儲一個指針饺律,即申請到的內(nèi)存空間。 -
x0
即是第一個參數(shù)的傳遞者跺株,也是返回值的存儲地方(傳遞口)复濒。注意下面這個類方法:+ (id)alloc { return _objc_rootAlloc(self); // self即Person }
3) 分支流程:
a、 hasCustomAWZ():
判斷當(dāng)前方法是否有默認(rèn)的allocWithZone
乒省;
b巧颈、canAllocFast():
這里最終直接返回false
,順著源碼具體分析一下:
- 順著
canAllocFast
點(diǎn)進(jìn)去:
bool canAllocFast() {
assert(!isFuture());
return bits.canAllocFast();
}
- Next:
#if FAST_ALLOC
// 省略代碼
bool canAllocFast() {
return bits & FAST_ALLOC;
}
#else
// 省略代碼
// 一般會走這里
bool canAllocFast() {
return false;
}
#endif
- 可以看到
FAST_ALLOC
是定義在另一個宏里的:
#if !__LP64__
// .....
#elif 1
// .....
// 一般會走這里
#else
// .....
#define FAST_ALLOC (1UL<<2)
#endif
- 可以判斷上面的宏只走
#elif 1
中的代碼袖扛,即FAST_ALLOC
一直沒有被define
砸泛,那么我們倒推一下就很容易理解為什么canAllocFast()
直接返回false
了。
c蛆封、來到_class_createInstanceFromZone
中:
-
bool hasCxxCtor = cls->hasCxxCtor();
是否有C++構(gòu)造函數(shù)唇礁。 -
bool hasCxxDtor = cls->hasCxxDtor();
是否有C++析構(gòu)函數(shù)。 -
bool fast = cls->canAllocNonpointer();
是否創(chuàng)建nonpointer
,這里為true
娶吞。 -
size_t size = cls->instanceSize(extraBytes);
申請內(nèi)存垒迂,這里有一個字節(jié)對齊的知識點(diǎn)。 -
obj = (id)calloc(1, size);
系統(tǒng)根據(jù)申請到的內(nèi)存大小去開辟相對應(yīng)的內(nèi)存空間給obj
對象妒蛇,更進(jìn)一步的代碼需要去malloc
源碼中去查看机断。 -
initInstanceIsa
創(chuàng)建對象。
// 字節(jié)對齊:至少16字節(jié)
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
2绣夺、init
探索
這里可以看到返回的是alloc
中創(chuàng)建的obj
吏奸,主要作用是預(yù)留給開發(fā)者在工廠模式中重寫初始化方法,方便自定義以及擴(kuò)展陶耍。
+ (id)init {
return (id)self;
}
3奋蔚、new
探索
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
四、LLDB命令
- register read: 讀取當(dāng)前寄存器;
- x/4xg p: 以16進(jìn)制截取4段,這里也可以是5xg泊碑、6xg坤按;
如有不當(dāng),歡迎指正馒过,感謝臭脓。