導(dǎo)語
在分析alloc && init && new原理之前浦译,我們先來看一下下面這段代碼
LGPerson *p1 = [LGPerson alloc];
LGPerson *p2 = [p1 init];
LGPerson *p3 = [p1 init];
//問題:p1,p2,p3打印結(jié)果是否一致幔戏?
//開辟的是同一塊空間罪帖,三個(gè)指針指向同一塊地址
LGNSLog(@"%@--%p--%p",p1,p1,&p1);
LGNSLog(@"%@--%p--%p",p2,p2,&p2);
LGNSLog(@"%@--%p--%p",p3,p3,&p3);
開辟的是同一塊空間卫旱,三個(gè)指針指向同一塊地址
视哑。
準(zhǔn)備工作
- 下載 objc4-781 源碼
一、alloc的探索
-
alloc的底層實(shí)現(xiàn)流程圖截屏2020-09-10 17.18.54.png
- 解析
1誊涯,首先根據(jù)main函數(shù)中的LGPerson類的alloc方法進(jìn)入alloc方法的源碼實(shí)現(xiàn)(即源碼分析開始)
//alloc源碼分析-第一步
+ (id)alloc {
return _objc_rootAlloc(self);
}
2挡毅,跳轉(zhuǎn)到_objc_rootAlloc
具體實(shí)現(xiàn)
//alloc源碼分析-第二步
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
3,跳轉(zhuǎn)到calloc源碼實(shí)現(xiàn)
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)// alloc 源碼 第三步
{
#if __OBJC2__ //有可用的編譯器優(yōu)化
/*
參考鏈接:http://www.reibang.com/p/536824702ab6
*/
// checkNil 為false暴构,!cls 也為false 跪呈,所以slowpath 為 false段磨,假值判斷不會(huì)走到if里面,即不會(huì)返回nil
if (slowpath(checkNil && !cls)) return nil;
//判斷一個(gè)類是否有自定義的 +allocWithZone 實(shí)現(xiàn)耗绿,沒有則走到if里面的實(shí)現(xiàn)
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// No shortcuts available. // 沒有可用的編譯器優(yōu)化
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
注意1:源碼中的fastpath
和slowpath
宏定義是判斷真假可能性的,以__builtin_expect(bool(x), 1)
為例表示 x
的值為真的可能性更大苹支;即 執(zhí)行if
里面語句的機(jī)會(huì)更大!
注意2:其中fastpath
中的cls->ISA()->hasCustomAWZ()
表示判斷一個(gè)類是否有自定義的 +allocWithZone
實(shí)現(xiàn),這里通過斷點(diǎn)調(diào)試误阻,是沒有自定義的實(shí)現(xiàn)债蜜,所以會(huì)執(zhí)行到 if
里面的代碼,即走到_objc_rootAllocWithZone
!
//x很可能為真究反, fastpath 可以簡稱為 真值判斷
#define fastpath(x) (__builtin_expect(bool(x), 1))
//x很可能為假寻定,slowpath 可以簡稱為 假值判斷
#define slowpath(x) (__builtin_expect(bool(x), 0))
4,跳轉(zhuǎn)到_objc_rootAllocWithZone
源代碼實(shí)現(xiàn)
id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)// alloc 源碼 第四步
{
// allocWithZone under __OBJC2__ ignores the zone parameter
//zone 參數(shù)不再使用 類創(chuàng)建實(shí)例內(nèi)存空間
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
5精耐,跳轉(zhuǎn)至_class_createInstanceFromZone的源碼實(shí)現(xià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)// alloc 源碼 第五步
{
ASSERT(cls->isRealized()); //檢查是否已經(jīng)實(shí)現(xiàn)
// 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;
//計(jì)算需要開辟的內(nèi)存大小狼速,傳入的extraBytes 為 0
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
//申請內(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) {
//將 cls類 與 obj指針(即isa) 關(guān)聯(lián)
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);
}
在該方法中主要分為三步:cls->instanceSize
、calloc
卦停、obj->initInstanceIsa
向胡,其中cls->instanceSize
用于計(jì)算某一個(gè)類需要開辟的空間的大小惊完;calloc
開辟空間并且返回指針的地址僵芹;obj->initInstanceIsa
將開辟的空間和指針地址進(jìn)行綁定。
二小槐、init
源碼探索
我們可以根據(jù)源碼得知init
是一個(gè)構(gòu)造方法淮捆,主要是用于給用戶提供構(gòu)造方法入口。這里能使用id強(qiáng)轉(zhuǎn)的原因本股,主要還是因?yàn)?內(nèi)存字節(jié)對(duì)齊后,可以使用類型強(qiáng)轉(zhuǎn)為你所需的類型桐腌。
+ (id)init {
return (id)self;
}
三拄显、new
的源碼探索
在我們的日常開發(fā)中我們也經(jīng)常使用new開初始化一個(gè)對(duì)象,從源碼中可以看出案站,new
其實(shí)就等價(jià)于[alloc init]
的操作躬审。
new 其實(shí)就等價(jià)于 [alloc init]
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}