對于iOS開發(fā)者來說alloc、init、new使用非常頻繁,那么接下來說說本人的理解。
準(zhǔn)備工作:
1、alloc做了什么?
-
根據(jù)源碼可得知流程,如下圖
alloc源碼流程圖.png
其中instanceSize、calloc、initInstanceIsa這三個函數(shù)最為重要,我們來分析一下。-
instanceSize函數(shù)
-
size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
根據(jù)斷點執(zhí)行cache.fastInstanceSize函數(shù)
size_t fastInstanceSize(size_t extra) const
{
ASSERT(hasFastInstanceSize(extra));
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
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
}
}
根據(jù)斷點執(zhí)行align16函數(shù)
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
總結(jié):instanceSize函數(shù)分析需要開辟內(nèi)存大小作用,其中align16函數(shù)使用了16字節(jié)對齊算法。
-
calloc函數(shù),開辟根據(jù)instanceSize函數(shù)分析出來的內(nèi)存大小
obj = (id)calloc(1, size);
-
initInstanceIsa函數(shù),初始化一個isa指針,并將isa指針指向申請的內(nèi)存地址,在將指針與類進行關(guān)聯(lián)
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
if (!nonpointer) {
isa = isa_t((uintptr_t)cls);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
}
2、init做了什么?
- (id)init {
return _objc_rootInit(self);
}
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本身,為啥還要寫init?init是一個構(gòu)造方法 ,是通過工廠設(shè)計(工廠方法模式),主要是用于給用戶提供構(gòu)造方法入口。
3、new做了什么?
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
源碼看new等價于alloc + init
4、補充16字節(jié)對齊算法理解
16字節(jié)對齊的需要
- 通常內(nèi)存是由一個個字節(jié)組成的,cpu在存取數(shù)據(jù)時,并不是以字節(jié)為單位存儲,而是以塊為單位存取,塊的大小為內(nèi)存存取力度。頻繁存取字節(jié)未對齊的數(shù)據(jù),會極大降低cpu的性能,所以可以通過減少存取次數(shù)來降低cpu的開銷
- 16字節(jié)對齊,是由于在一個對象中,第一個屬性isa占8字節(jié),當(dāng)然一個對象肯定還有其他屬性,當(dāng)無屬性時,會預(yù)留8字節(jié),即16字節(jié)對齊,如果不預(yù)留,相當(dāng)于這個對象的isa和其他對象的isa緊挨著,容易造成訪問混亂
16字節(jié)對齊后,可以加快CPU讀取速度,同時使訪問更安全,不會產(chǎn)生訪問混亂的情況
例子align16(9)
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
- 9 + 15 = 24 =
0000 0000 0001 1000 - 15 =
0000 0000 0000 1111,~15(取反15) =1111 1111 1111 0000 -
&的算法規(guī)則:兩個為1才為1,則為0。所以24 & ~15=0000 0000 0001 0000= 16 -
align16(9)= 16
