[[NSObject alloc] init]兩段式構(gòu)造
1徘跪、對象分配,方法有alloc和allocWithZone:
流程圖:
alloc方法流程.png
經(jīng)過上面的一系列判斷,過程最終是_class_createInstanceFromZone函數(shù)
static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
if (!cls) return nil;
assert(cls->isRealized());
bool hasCxxCtor = cls->hasCxxCtor();// 判斷當前class或者superclass 是否有.cxx_construct構(gòu)造方法的實現(xiàn)
bool hasCxxDtor = cls->hasCxxDtor();// 判斷判斷當前class或者superclass 是否有.cxx_destruct析構(gòu)方法的實現(xiàn)
bool fast = cls->canAllocNonpointer();// 是對 isa 的類型的區(qū)分琅攘,如果一個類和它父類的實例不能使用isa_t 類型的 isa 的話垮庐,fast 就為 false,但是在 Objective-C 2.0 中坞琴,大部分類都是支持的
// 獲得分配的內(nèi)存的大小
size_t size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (!zone && fast) {
obj = (id)calloc(1, size); // 分配內(nèi)存空間哨查,calloc( )函數(shù)會默認的把申請出來的空間初始化為0或者nil
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor); // 初始化Isa指針
}
else {
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls); // 初始化Isa指針
}
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
initIsa(cls, true, hasCxxDtor);
}
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
if (!nonpointer) {
isa.cls = cls;
} else {
isa_t newisa(0);
newisa.bits = ISA_MAGIC_VALUE;
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
isa = newisa;
}
}
initIsa解析
1、newisa.bits = ISA_MAGIC_VALUE;
從ISA_MAGIC_VALUE的定義中可以看到這個字段初始化了兩個部分剧辐,一個是magic字段(6位:111011)寒亥,一個是nonpointer字段(1位:1),magic字段用于校驗荧关,nonpointer之前已經(jīng)詳細分析過了溉奕。
2、newisa.has_cxx_dtor = hasCxxDtor;
這個字段存儲類是否有c++析構(gòu)器羞酗。
3腐宋、newisa.shiftcls = (uintptr_t)cls >> 3;
將cls右移3位存到shiftcls中,從isa_t的結(jié)構(gòu)體中也可以看到低3位都是用來存儲其他信息的,既然可以右移三位胸竞,那就代表類地址的低三位全部都是0欺嗤,否則就出錯了,補0的作用應該是為了字節(jié)對齊卫枝。
uintptr_t nonpointer : 1; // 0 表示普通的 isa 指針煎饼,1 表示使用優(yōu)化,存儲引用計數(shù)
uintptr_t has_assoc : 1; // 表示該對象是否包含 associated object校赤,如果沒有吆玖,則析構(gòu)時會更快
uintptr_t has_cxx_dtor : 1; // 表示該對象是否有 C++ 或 ARC 的析構(gòu)函數(shù),如果沒有马篮,則析構(gòu)時更快
uintptr_t shiftcls : 33; // 類的指針
uintptr_t magic : 6; // 固定值為 0xd2沾乘,用于在調(diào)試時分辨對象是否未完成初始化。
uintptr_t weakly_referenced : 1; // 表示該對象是否有過 weak 對象浑测,如果沒有翅阵,則析構(gòu)時更快
uintptr_t deallocating : 1; // 表示該對象是否正在析構(gòu)
uintptr_t has_sidetable_rc : 1; // 表示該對象的引用計數(shù)值是否過大無法存儲在 isa 指針
uintptr_t extra_rc : 19; // 存儲引用計數(shù)值減一后的結(jié)果
isa結(jié)構(gòu)
當我們通過 alloc 或 allocWithZone 方法創(chuàng)建對象時做以下 3 件事:
1、分配內(nèi)存迁央,會遍歷該對象所有的成員變量掷匠,通過成員變量的類型來計算所需占用的內(nèi)存
2、將該新對象的引用計數(shù) (Retain Count) 設置成 1岖圈。
3讹语、將該新對象的 isa 成員變量指向它的類對象。
4蜂科、將該新對象的所有其它成員變量的值設置成零顽决。(根據(jù)成員變量類型,零有可能是指 nil 或 Nil 或 0.0)
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;
}
_objc_rootInit 函數(shù)直接就將 obj 返回了,所以 -init 方法其實什么都沒有做逐抑。
但是開發(fā)者留下的注釋非常值得玩味鸠儿,說到很多類沒有使用 [super init],所以這個函數(shù)非巢薨保靠不捉俊(很可能不被調(diào)用)∶看起來應該是個歷史遺留問題田晚,從上面對 +alloc 的分析也能看出,+alloc 把所有的工作都做完了(初始化了 isa国葬,我個人認為理論上初始化 isa 應該是 -init 的工作)贤徒。
3芹壕、new方法
new實際上是集alloc和init于一身,它創(chuàng)建了對象并初始化了對象接奈。它的實現(xiàn)如下:
// 源碼
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
// 可以這么理解
+ (instancetype)new {
return [[self alloc] init];
}