本文只做個人筆記使用旗国,引用部分會在文末著名來源
alloc+inti整體流程:
alloc核心三步:
1.先計算出需要的內(nèi)存大小空間;
2.向系統(tǒng)申請開辟內(nèi)存距淫,返回地址指針袖扛;
3.將cls類與obj指針關(guān)聯(lián);
在instanceSize
的源碼中實現(xiàn)內(nèi)存大小
size_t instanceSize(size_t extraBytes) const {
//編譯器快速計算內(nèi)存大小
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
// 計算類中所有屬性的大小 + 額外的字節(jié)數(shù)0
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
//如果size 小于 16弯蚜,最小取16
if (size < 16) size = 16;
return size;
}
通過斷點調(diào)試孔轴,會執(zhí)行cache.fastInstanceSize
方法,快速計算內(nèi)存大小
在fastInstanceSize
源碼中碎捺,會執(zhí)行到align16
size_t fastInstanceSize(size_t extra) const
{
ASSERT(hasFastInstanceSize(extra));
//Gcc的內(nèi)建函數(shù) __builtin_constant_p 用于判斷一個值是否為編譯時常數(shù)路鹰,如果參數(shù)EXP 的值是常數(shù),函數(shù)返回 1收厨,否則返回 0
if (__builtin_constant_p(extra) && extra == 0) {
return _flags & FAST_CACHE_ALLOC_MASK16;
} else {
size_t size = _flags & FAST_CACHE_ALLOC_MASK;
// remove the FAST_CACHE_ALLOC_DELTA16 that was added
// by setFastInstanceSize
//刪除由setFastInstanceSize添加的FAST_CACHE_ALLOC_DELTA16 8個字節(jié)
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
}
}
在align16
的源碼中晋柱,這個方法是16字節(jié)對齊算法
//16字節(jié)對齊算法
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
內(nèi)存對齊原則:
1:數(shù)據(jù)成員對?規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第
?個數(shù)據(jù)成員放在offset為0的地?诵叁,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員??或者成員的?成員??(只要該成員有?成員雁竞,?如說是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(?如int為4字節(jié),則要從4的整數(shù)倍地址開始存儲黎休。 min(當前開始的位置m n) m = 9 n = 4 9 10 11 12
2:結(jié)構(gòu)體作為成員:如果?個結(jié)構(gòu)?有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最?元素??的整數(shù)倍地址開始存儲.(struct a?存有struct b,b?有char,int ,double等元素,那b應該從8的整數(shù)倍開始存儲.)
3:收尾?作:結(jié)構(gòu)體的總??,也就是sizeof的結(jié)果,必須是其內(nèi)部最?
成員的整數(shù)倍浓领,不?的要補?玉凯。
為什么需要16字節(jié)對齊
需要字節(jié)對齊的原因,有以下幾點:
通常內(nèi)存是由一個個字節(jié)組成的联贩,cpu在存取數(shù)據(jù)時漫仆,并不是以字節(jié)為單位存儲,而是以塊為單位存取泪幌,塊的大小為內(nèi)存存取力度盲厌。頻繁存取字節(jié)未對齊的數(shù)據(jù),會極大降低cpu的性能祸泪,所以可以通過減少存取次數(shù)來降低cpu的開銷
16字節(jié)對齊吗浩,是由于在一個對象中,第一個屬性isa占8字節(jié)没隘,當然一個對象肯定還有其他屬性懂扼,當無屬性時,會預留8字節(jié)右蒲,即16字節(jié)對齊阀湿,如果不預留,相當于這個對象的isa和其他對象的isa緊挨著瑰妄,容易造成訪問混亂
16字節(jié)對齊后陷嘴,可以加快CPU讀取速度,同時使訪問更安全间坐,不會產(chǎn)生訪問混亂的情況
字節(jié)對齊-總結(jié)
在字節(jié)對齊算法中灾挨,對齊的主要是對象,而對象的本質(zhì)則是一個 struct objc_object的結(jié)構(gòu)體竹宋,
結(jié)構(gòu)體在內(nèi)存中是連續(xù)存放的劳澄,所以可以利用這點對結(jié)構(gòu)體進行強轉(zhuǎn)。
蘋果早期是8字節(jié)對齊蜈七,現(xiàn)在是16字節(jié)對齊浴骂。
總結(jié):
1.通過對alloc
源碼的分析可以得知alloc的只要目的就是開辟內(nèi)存
,而且開辟的內(nèi)存需要使用16字節(jié)對齊的算法
宪潮,現(xiàn)在開辟的內(nèi)存基本上都是16的整數(shù)倍溯警;
2.開辟內(nèi)存的核心步驟有三步:計算內(nèi)存大小-申請內(nèi)存-將isa與cls關(guān)聯(lián)
init源碼探索
類方法init
+ (id)init {
return (id)self;
}
這里的init是一個構(gòu)造方法 ,是通過工廠設計(工廠方法模式),主要是用于給用戶提供構(gòu)造方法入口狡相。這里能使用id強轉(zhuǎn)的原因梯轻,主要還是因為 內(nèi)存字節(jié)對齊后,可以使用類型強轉(zhuǎn)為你所需的類型
實例方法init
- (id) init {
return _objc_rootInit(self);
}
進入_objc_rootInit源碼
id _objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
返回的是傳入的 self
本身
new源碼
一般在開發(fā)中尽棕,初始化除了init
喳挑,還可以使用new
,兩者本質(zhì)上并沒有什么區(qū)別,以下是objc中new的源碼實現(xiàn)伊诵,通過源碼可以得知单绑,new函數(shù)中直接調(diào)用了callAlloc
函數(shù)(即alloc中分析的函數(shù)),且調(diào)用了init函數(shù)曹宴,所以可以得出new 其實就等價于 [alloc init]的結(jié)論
.
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
一般開發(fā)中并不建議使用new搂橙,主要是因為有時會重寫init方法做一些自定義的操作,例如 initWithXXX
笛坦,會在這個方法中調(diào)用[super init]
区转,用new初始化可能會無法走到自定義的initWithXXX
部分。