[NSObject alloc]方法為什么沒有走源碼摇幻?颓芭?顷锰?
如上圖發(fā)現(xiàn)并沒有走alloc的源碼,通過匯編調(diào)試(Debug->Debug Workflow->Always Show Disassembly)發(fā)現(xiàn)調(diào)用了objc_alloc方法
在
objc_alloc
中斷點亡问,發(fā)現(xiàn)[NSObject alloc]中調(diào)用了
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
那系統(tǒng)源碼是在什么時候調(diào)用objc_alloc方法呢官紫,我們不得而知(需要查看llvm)。在NSObject.mm源碼中搜不到objc_alloc的被調(diào)用次數(shù)
[DCPerson alloc]調(diào)用過程中州藕,alloc調(diào)用了兩次,第一次調(diào)用是objc_alloc----->callAlloc----->return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));進入objc_msgSend的alloc方法(第二次調(diào)用)----->alloc(return _objc_rootAlloc(self);)----->進入到callAlloc...對象的初始化階段
- [NSObject alloc]流程圖如下
- [DCPerson alloc]流程圖如下
對象的內(nèi)存結(jié)構
為確保內(nèi)存不浪費束世,不滿8字節(jié)的屬性大小會在內(nèi)存中緊密相連,比如屬性age,4字節(jié)床玻,c1毁涉,1字節(jié),c2锈死,1字節(jié)贫堰,共用8字節(jié)的內(nèi)存地址0x0000001200006261,如下圖:
內(nèi)存對齊原則
- 1 數(shù)據(jù)成員對?規(guī)則:結(jié)構(struct)(或聯(lián)合(union))的數(shù)據(jù)成員待牵,第
一個數(shù)據(jù)成員放在offset為0的地方其屏,以后每個數(shù)據(jù)成員存儲的起始位置要
從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數(shù)組缨该,
結(jié)構體等)的整數(shù)倍開始(比如int為4字節(jié),則要從4的整數(shù)倍地址開始存
儲偎行。 min(當前開始的位置m,n當前元素的大小占用字節(jié)數(shù)),若m=9,n=4蛤袒,m%n !=0,往后偏移熄云,知道遇到12%4=0,從12開始存儲
9 10 11跳過 12 13 14 15
- 2.結(jié)構體作為成員:如果一個結(jié)構里有某些結(jié)構體成員,則結(jié)構體成員要從
其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲.(struct a里存有struct b,b
里有char,int ,double等元素,那b應該從8的整數(shù)倍開始存儲.)
- 3.收尾工作:結(jié)構體的總大小,也就是sizeof的結(jié)果,.必須是其內(nèi)部最大
成員的整數(shù)倍.不足的要補?
結(jié)構體嵌套結(jié)構體內(nèi)存對齊
結(jié)構體作為成員:如果一個結(jié)構里有某些結(jié)構體成員,則結(jié)構體成員要從
其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲.(struct a里存有struct b,b
里有char,int ,double等元素,那b應該從8的整數(shù)倍開始存儲.)
struct DCStruct1{
double a; //8 (0-7)
char b; //1 (8)
int c; //4 (9,10,11,12-15)
short d; //2 (16-17)
}struct1;
//18 ------- 24
struct DCStruct2{
double a; //8 (0-7)
int b; //4 (8-11)
char c; //1 (12)
short d; //2 (13,14-15)
}struct2;
//16
struct DCStruct3 {
double a; //8 (0-7)
float b; //4 (8-11)
int c; //4 (12-15)
short d; //2 (16-17)
char e; //1 (18)
struct DCStruct2 struct0;// 24 double(24-31) int(32-35) char(36) short(37,38-39)
} struct3;
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
DCPerson *person = [DCPerson alloc];
NSLog(@"%@",person);
NSLog(@"%lu----%lu",sizeof(struct1),sizeof(struct2));
NSLog(@"%lu",sizeof(struct3));
}
return 0;
}
//輸出
2020-09-09 01:21:04.033630+0800 DCTestObjc[34891:515298] <DCPerson: 0x1005713a0>
2020-09-09 01:21:04.034025+0800 DCTestObjc[34891:515298] 24----16
2020-09-09 01:21:04.034071+0800 DCTestObjc[34891:515298] 40
源碼分析-對象需要的真正內(nèi)存
一個對象真正需要的內(nèi)存是8字節(jié)對齊
//申請開辟內(nèi)存空間是 16字節(jié)對齊
//class_getInstanceSize 對象需要的真正的內(nèi)存 8字節(jié)對齊
DCPerson *person = [DCPerson alloc];
person.name = @"Cloud";
// person.nickName = @"fish";
NSLog(@"%@ - %lu - %lu - %lu",person,sizeof(person),class_getInstanceSize([DCPerson class]),malloc_size((__bridge const void *)(person)));
2020-10-01 20:11:51.252328+0800 DCTestObjc[89404:480515] <DCPerson: 0x100551d60> - 8 - 40 - 48
calloc流程分析
跟斷點會卡在zone->calloc(zone,num_items,size);
此時執(zhí)行p zone->calloc能找到下一步源碼
(lldb) p zone->calloc
(void *(*)(_malloc_zone_t *, size_t, size_t)) $0 = 0x00000001002c7bfe (.dylib`default_zone_calloc at malloc.c:331)
(lldb) p zone->calloc
(void *(*)(_malloc_zone_t *, size_t, size_t)) $1 = 0x00000001002cdb0a (.dylib`nano_calloc at nano_malloc.c:878)
(lldb)
開辟內(nèi)存16字節(jié)對齊
對一個數(shù)加上15汗盘,右移4位之后再左移4位——>后四位抹零皱碘,即16字節(jié)對齊
#define SHIFT_NANO_QUANTUM 4
#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM // 16
size_t k, slot_bytes;
if (0 == size) {
size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
}
k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
slot_bytes = k << SHIFT_NANO_QUANTUM; // multiply by power of two quanta size
*pKey = k - 1; // Zero-based!
return slot_bytes;