開發(fā)中,我們經(jīng)常使用到[[xxx alloc] init]
,但卻沒有真正理解過alloc,init,現(xiàn)在進(jìn)行深入了解一下搓扯。
alloc,init的功能
1.先看看這樣的一個(gè)例子
//創(chuàng)建一個(gè)Person類,然后引用對(duì)應(yīng)頭文件
Person *p1 = [Person alloc];
Person *p2 = [p1 init];
Person *p3 = [p1 init];
NSLog(@"%p-%p", p1, &p1);
NSLog(@"%p-%p", p2, &p2);
NSLog(@"%p-%p", p3, &p3);
運(yùn)行結(jié)果如圖:- p1,p2,p3的指向地址都相同
- p1,p2,p3的地址不同
2.alloc與init的關(guān)系如圖
- alloc生成對(duì)象地址礁芦。
- init分配指針地址,并指向?qū)ο蟮刂?/li>
alloc流程
需要看到alloc流程,可到蘋果開源網(wǎng)站下載。
如果要將源碼進(jìn)行編譯調(diào)試,可參見
對(duì)源碼進(jìn)行分析
從alloc方法一步一步點(diǎn)進(jìn)去我們分別能看到:
//NSObject.mm文件中 +alloc方法
+ (id)alloc {
return _objc_rootAlloc(self);
}
//NSObject.mm文件中 _objc_rootAlloc(Class cls)方法
id _objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
//NSObject.mm文件中 callAlloc方法
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// No shortcuts available.
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
//objc-runtime-new.mm文件中 _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)方法
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
//objc-runtime-new.mm文件中 _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);
}
1. 前幾步一條路進(jìn)入可以不提棍矛,在callAlloc
方法中蕴掏,出現(xiàn)了分支slowpath
和fastpath
门坷,快速路徑與慢速路徑默勾,可以理解為大多數(shù)情況走快速路徑,極少情況走慢速路徑乍恐,這樣寫便于運(yùn)行效率的提升评疗,所以我們繼續(xù)研究_objc_rootAllocWithZone方法。
2. 緊接著我們到了關(guān)鍵的_class_createInstanceFromZone方法:
- _class_createInstanceFromZone第一個(gè)研究的方法
size = cls->instanceSize(extraBytes);
inline 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;
}
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īng)過調(diào)試茵烈,我們能看到進(jìn)入fastInstanceSize
方法壤巷,然后執(zhí)行return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
很明顯,這是在進(jìn)行內(nèi)存對(duì)齊(下一章節(jié)具體講解)
3. 得到一個(gè)16字節(jié)倍數(shù)大小size瞧毙,然后執(zhí)行obj = (id)calloc(1, size);
分配內(nèi)存空間(ps:if (zone)
現(xiàn)已廢棄胧华,不做過深研究)
當(dāng)我們?cè)趫?zhí)行obj = (id)calloc(1, size);
后我們答應(yīng)obj可以看到:
而我們一個(gè)對(duì)象的打印的格式應(yīng)該為
<Person: 0x10060de90>
,所以calloc只是分配了一個(gè)地址空間,并沒有創(chuàng)建新的指針指向它宙彪。也證明了前面alloc的說法
4.當(dāng)我們執(zhí)行完obj->initInstanceIsa(cls, hasCxxDtor);
我們拿到我們想要的結(jié)果:
綜上alloc運(yùn)行流程如圖: