探究:
1.objc_alloc流程
2.init操作
3.開辟內存
YGPerson * p1 = [YGPerson alloc];
YGPerson * p2 = [p1 init];
YGPerson * p3 = [p1 init];
/**
<YGPerson: 0x600003d14300>--0x600003d14300--0x7ffee5b5d0e8 //p1
<YGPerson: 0x600003d14300>--0x600003d14300--0x7ffee5b5d0e0 //p2
<YGPerson: 0x600003d14300>--0x600003d14300--0x7ffee5b5d0d8 //p3
*/
-
alloc
開辟內存.
- objc_alloc流程
1.alloc
-_objc_rootAlloc
-callAlloc
-objc_msgSend
2.callAlloc
函數內部
cls->ISA()->hasCustomAWZ()
: 是否重寫+ alloc
或+ allocWithZone
方法,是否執(zhí)行消息轉發(fā)流程唱蒸。
_objc_rootAllocWithZone
中_class_createInstanceFromZone
核心函數
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
// 幾個核心函數如下
// 計算對象的大小
size_t size;
size = cls->instanceSize(extraBytes);
//開辟內存
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
// 初始化cls信息 obj與cls綁定
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);
}
return obj;
}
流程圖:
-
init
操作.
init
是一個工廠模式盗誊,作為析構函數,給子類重寫使用,提供接口便于擴展
new
相當于alloc init
操作 源碼如下:
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
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;
}
/**
直接將self返回,實際上就是為子類提供一個工廠方法否纬,讓子類 做自定義的初始化操作
*/
- p1 p2 p3三個指針指向同一塊內存,三個指針地址為連續(xù)的.
- 字節(jié)對齊及其原理
首先計算所需內存大小
inline 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;
}
uint32_t unalignedInstanceSize() const {
ASSERT(isRealized());
return data()->ro()->instanceSize;
}
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
// 重點
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
# define WORD_MASK 7UL
舉例:
[Person alloc]
時,所需內存大小size
由alignedInstanceSize
決定
unalignedInstanceSize
是8字節(jié)芬首,來源于Person的父類NSObject
中的Class isa
.
由上面源碼 得知 word_align算法
(8 + 7) & ~7 即 15 & ~7
0000 1111 (15的二進制 )
0000 0111 (7的二進制)
1111 1000 (~7的二進制)
15 & ~7 與運算如下
0000 1111
1111 1000
結果
0000 1000 轉換為二進制是8
8字節(jié)對齊畴博,取8的整數,alignedInstanceSize結果為8
但是
最終計算結果 if (size < 16) size = 16可知频丘,size小于16時办成,結果取16
源碼上面有體現。
內存對齊的原則 補充
1.結構體(struct)或者聯合體(union)的數據成員搂漠,第一個數據成員放在offset為0的地方以后每個數據成員存儲的起始位置要從該成員大小或者成員的子成員大杏芈(只要該成員有子成員,比如說是數組桐汤,結構體等)的整數倍開始(比如int為4字節(jié)而克,則要從4的整數倍地址開始存儲)。
2.如果一個結構里有某些結構體成員怔毛,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲员萍。(struct a里存有struct b,b里有char拣度,int碎绎,double等元素,那b應該從8的整數倍開始存儲)抗果。
3.結構體的總大小筋帖,也就是sizeof的結果,必須是其內部最大成員的整數倍冤馏。不足的要補齊日麸。-
基本數據類型的內存大小
計算機基礎
十六進制數字與二進制數字的對應關系如下:
0000 -> 0、0001 -> 1逮光、0010 -> 2代箭、0011 -> 3墩划、
0100 -> 4、0101 -> 5梢卸、0110 -> 6走诞、0111 -> 7、
1000 -> 8蛤高、1001 -> 9蚣旱、1010 -> A、1011 -> B戴陡、
1100 -> C塞绿、1101 -> D、1110 -> E恤批、1111 -> F异吻。
因此,1個16進制數對應4個二進制數位喜庞,2個16進制數位對應8個二進制數位诀浪,及1個字節(jié)
計算得出內存占用大小
struct Person1 {
char a; // [0]
double b; // [8 16)
int c; // [16 20)
short d; // [20 22) ----->24
}MyPerson1;
struct Person2 {
long b; // [0 8)
char a; // [8]
int c; // [12 16)
short d; // [16 17] ----->24
}MyPerson2;
struct Person3 {
long b;//[0 8)
int c;//[8 12)
char a;//[12]
short d;//[14 15] -------->16
}MyPerson3;
struct Person4 {
double a; //[0,7]
int b; //[8,11]
char c; //[12]
short d; //跳過13 [14,15]
int e; // [16,19]
struct Person1 str; //根據準則2,Person1最大元素為`double`類型延都,所以從24開始雷猪。根據`Person1 `分配的時候24個字節(jié),所以str為[24,47]
} Person4;
x/nuf ------->x/4gx
n 表示要顯示的內存單元的個數
u 表示一個地址單元的長度
b 表示單字節(jié)
h 表示雙字節(jié)
w 表示4字節(jié)
g 表示8字節(jié)
f 表示顯示方式晰房,可取以下值:
x 按十六進制格式顯示變量
d 按十進制格式顯示變量
u 按十進制格式顯示無符號整型
o 按八進制格式顯示變量
t 按二進制格式顯示變量
a 按十六進制格式顯示變量
i 按指令地址格式顯示變量
c 按字符格式顯示變量
f 按浮點數格式顯示變量