寫在最前:記錄一個最近看到的一個問題,實例對象是alloc建立的仍是init建立的?寫的很差,還請看到的人輕噴!sass
有一個Person
類,聲明一個age
屬性bash
Person *p = [Person alloc];
p.age = 10;
Person *p1 = [p init];
Person *p2 = [p init];
複製代碼
首先有兩個疑問: Q1: p1和p2是相同的嗎? Q2: p1.age和p2.age的值是多少?(0或者10) 咱們先來看一下結果Q1:函數
p1=<Person: 0x6000029bca80>
p2=<Person: 0x6000029bca80>
複製代碼
經過其內存地址能夠看到這兩個對象是同樣的(可是沒存地址同樣就足以證實這兩個對象是同一個東西嗎,這不必定,p1,p2在初始化的時候,初始化的內容可能不同)ui
結果Q2:spa
p1.age=10
p2.age=10
複製代碼
經過這個結果好像能夠看出:指針
Person *p1 = [p init];
Person *p2 = [p init];
等價於
Person *p1 = p;
Person *p2 = p;
哦,彷佛init方法沒什麼用啊?
複製代碼
那咱們就看一下alloc方法幹了些什麼 經過objc的NSObject代碼:調試
1.
+ (id)alloc {
reeturn _objc_rootAlloc(self);
}
可知返回的這個self應該就是指當前的這個類對象
2.在進一步跟進去_objc_rootAlloc
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*chechNil*/, true/*allocWithZone*/);
}
3.跟進去callAlloc
static id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
if (checkNil && !cls) return nil;
if (!cls->ISA()->hasCustomAWZ()) {
// No alloc/allocWithZone implementation. Go straight to the allocator.
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFast's summary if (fastpath(cls->canAllocFast())) { // No ctors, raw isa, etc. Go straight to the metal. bool dtor = cls->hasCxxDtor(); id obj = (id)calloc(1, cls->bits.fastInstanceSize()); if (slowpath(!obj)) return callBadAllocHandler(cls); obj->initInstanceIsa(cls, dtor); return obj; } else { // Has ctor or raw isa or something. Use the slower path. id obj = class_createInstance(cls, 0); if (slowpath(!obj)) return callBadAllocHandler(cls); return obj; } } // No shortcuts available. if (allocWithZone) return [cls allocWithZone:nil]; return [cls alloc]; } 4.這裏主要用到的代碼就是id obj = class_createInstance(cls, 0);跟進去看到 id class_createInstance(Class cls, size_t extraBytes) { return _class_createInstanceFromZone(cls, extraBytes, nil); } 5.繼續跟進去_class_createInstanceFromZone static id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct = true, size_t *outAllocatedSize = nil) { if (!cls) return nil;// 首先是判斷是否爲空 assert(cls->isRealized());// 檢查是否已經realize 了 // Read class's info bits all at once for performance
bool hasCxxCtor = cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size = cls->instanceSize(extraBytes); // 這一步是獲取對象大小的
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (!zone && fast) {
obj = (id)calloc(1, size); // 分配空間
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor);// 初始化
}
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);
}
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
最主要的代碼就是
obj = (id)calloc(1, size);
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor);
複製代碼
事實上咱們看不到蘋果的源碼,下面經過彙編代碼來看一下(真機調試),首先在Person *p = [Person alloc];處打個斷點,當運行到該斷點時,在Xcode菜單欄-->Debug-->DebugWorkflow-->Always Show Disassembly選中,code
能夠看到當前斷點走到了23行(;分號表示說明,分號後面是說明內容)咱們都知道,若是把[Person alloc]換成objc_msgSend的寫法應該是objc_msgSend(Person,@selector(alloc)),咱們看到第26行,(這裏的bl至關於函數跳轉,這一行指令就至關於在調用函數)接着在26行處打斷點,咱們經過寄存器來看一下,這個msgSend()的兩個參數是什麼? (調用函數的時候,函數的參數是放在x0--x7這8個寄存器中的,關於寄存器的內容,我也只是淺層的瞭解,很差意思,後續會有補充) 如今咱們能夠知道26行的代碼,其實就是在執行[Person alloc],下面咱們alloc方法裏下個符號斷點, 以後過掉第26行的代碼, 能夠看到斷點來到了NSObject alloc(當前Person類並無重寫alloc方法,確定會來到其父類的alloc),能夠看到這句代碼調用的是_objc_rootAlloc,重複上一步,咱們在下一個_objc_rootAlloc的符號斷點,而後過掉alloc的斷點, 能夠看到rootAlloc第一次的函數調用時在第21行有函數跳轉class_createInstance方法,一樣的給class_createInstance添加一個符號斷點,過掉_objc_rootAlloc的斷點,[圖片上傳中...(屏幕快照 2019-04-25 下午10.41.03.png-dafb56-1556203291738-0)] 咱們能夠看到在第44行的ret(ret就至關於return),由前面咱們知道調用函數的時候,函數的參數是放在x0--x7這8個寄存器中的,當函數調用完成以後,x0寄存器存放的是返回值, 能夠看出此時x0存放的實際上是Person對象的指針,接下來咱們在第44行ret處添加斷點,並過掉class_createInstance的斷點,咱們再來看一下寄存器x0存放的內容 而後po獲得的這個地址,能夠獲得<Person: 0x1cc00d500>,此時返回的是一個person對象,此時此刻,一個完整的對象就建立完成了。因此說alloc纔是真正建立實例對象的方法。