萬物皆對(duì)象,那對(duì)象是怎么創(chuàng)建出來的呢?帶著這個(gè)問題,我們首先得了解一個(gè)類
[[LGPerson alloc] init]在創(chuàng)建的過程中alloc做了些什么?init做了些什么?alloc是怎樣開辟內(nèi)存的?
1.我們首先來看一個(gè)例子:

image.png
- 根據(jù)打印的信息,我們可以看出p1,p2,p3的指針地址是相同的,但是他們的內(nèi)存地址卻是不同的,為什么是這樣呢?這就是接下來我們要討論的
alloc 和 init到底做了些什么?
準(zhǔn)備工作:
- 下載 objc4-781 源碼
- 編譯源碼,這期間會(huì)出現(xiàn)各類問題,解決的方法請(qǐng)參考這篇文章objc4_debug

image.png
通過配置好的源碼,我們可以打斷點(diǎn)一步一步的往下走:
- 在
[LGPerson alloc]方法打上斷點(diǎn),會(huì)走到alloc方法
- 在
+ (id)alloc {
return _objc_rootAlloc(self);
}
- 進(jìn)入到
_objc_rootAlloc方法
- 進(jìn)入到
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
- 進(jìn)入到
callAlloc方法
- 進(jìn)入到
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));
}
- 進(jìn)入到
_objc_rootAllocWithZone方法
- 進(jìn)入到
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);
}
- 進(jìn)入到
_class_createInstanceFromZone方法, 這里面主要實(shí)現(xiàn)了 3個(gè)方法-
cls->instanceSize計(jì)算出需要開辟的內(nèi)存空間大小 -
calloc向系統(tǒng)申請(qǐng)開辟內(nèi)存,返回地址指針 -
obj->initInstanceIsa將 cls類 與 obj指針(即isa)進(jìn)行關(guān)聯(lián)
-
- 進(jìn)入到
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 {
// alloc 開辟內(nèi)存的地方
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);
}
- 我們可以通過斷點(diǎn)調(diào)試進(jìn)入計(jì)算內(nèi)存大小的方法
cls->instanceSize
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;
}
- 斷點(diǎn)會(huì)進(jìn)入
cache.fastInstanceSize方法
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);
}
}
- 斷點(diǎn)繼續(xù)下一步,會(huì)走到
align16方法, 這個(gè)方法的作用是 16字節(jié)對(duì)齊算法。以前的都是8字節(jié)對(duì)齊,由于內(nèi)存的消耗越來越大了,8字節(jié)對(duì)齊已經(jīng)不能滿足用戶需求了,所以現(xiàn)在都是16字節(jié)對(duì)齊。
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
接下來我們分析init方法做了什么
- 根據(jù)源碼我們可以進(jìn)入到
init的類方法實(shí)現(xiàn),這里是一個(gè)工廠方法,返回一個(gè)強(qiáng)轉(zhuǎn)為id類型的構(gòu)造方法。
+ (id)init {
return (id)self;
}
- 通過 點(diǎn)擊
init方法,我們會(huì)計(jì)入到init的實(shí)例方法, 會(huì)返回_objc_rootInit方法,跳轉(zhuǎn)進(jìn)入到方法實(shí)現(xiàn)中,會(huì)返回obj對(duì)象本身
- (id) init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
return obj;
}
補(bǔ)充: 我們可以看一下 new方法的源碼是怎樣實(shí)現(xiàn)的。
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
- 通過源碼我們可以看出,調(diào)用
new方法會(huì)返回callAlloc方法調(diào)用init方法。實(shí)際上就是調(diào)用了[alloc init]方法。