OC 对象 - 内存对齐和 isa指针结构

391 阅读3分钟

对象和指针的地址分配

main() {
Person *p = [Person alloc];
}

p代表创建对象的指针,而指针地址是在栈中系统自动分配内存地址,只要在方法内的变量都是分配在栈上的。而 p 所指向的地址即是对象(开辟的内存在堆上)

结构体-内存对齐原则

1:数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储。 min(当前开始的位置mn) m=9 n=4 9 10 11 12

2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)

3:收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大 课程研发:Cooci老师成员的整数倍.不足的要补⻬。

数据类型对应的字节

bool/char:1
short:2
int/float/long:4
nsinteger/cgfloat/指针/nsstring/double/long long:8位字节

例如:
struct RStruct {
int a; //4
char b; //1
short c; //2
}Rstruct;
这次直接算:a-【0-3】,c-【4】,c-【6-7】,其内部需要的大小是7,但是其内部成员变量最大4,所以整体需要的是4的倍数既:8

总结:结构体的内存对齐市按照属性排列下来的。

对象-内存对齐的优化

从上面看到:结构体的内存对齐市按照属性排列下来的,而对象的内存对齐却不是,而是编译器堆对象的内存做了优化.对象属性在存储的时候是按照属性中最大值得倍数对齐(一般为8),而16字节对齐则是针对整个对象。因为系统开辟的内存如果按照属性大小来分配,可能会导致内存溢出。

isa 指针结构

在arm64架构下,指针在内存中占8个字节/byte,即64位/bit(1byte=8bit),64比特位中存储的内容有:

位域的声明:

  1. NOPOINTER_ISA:标记是否开启了指针优化,是否是纯的ISA指针,还是非指针型NOPOINTER_ISA指针,0代表纯isa指针,1代表不止是类对象指针,还包含了类信息、对象的引用计数等\
  2. has_assoc:标记是否有关联对象\
  3. has_cxx_dtor:标记对象是否使用到的C++相关内容,在ARC环境下标记对象是否通过ARC来管理的\
  4. shiftcls:标记当前对象的类对象的指针地址\
  5. magic:用于调试器判断当前对象是真的对象还是没有初始化的空间\
  6. weakly_referenced:标记对象是否有弱引用指针\
  7. deallocating:标记对象是否正在进行dealloc操作\
  8. has_sidetable_rc:标记是否有sitetable结构用于存储引用计数\
  9. extra_rc:标记对象的引用计数(首先会存储在该字段中,当到达上限后,在存入对应的引用计数表中) 例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10, 则需要使用到上面的 has_sidetable_rc。

指针这么设计是为类优化内存,节省空间,避免造成浪费,所以在空余的地方加入对象的其他信息

isa 和 内存的绑定

指针是一个 isa_t 类型, 一个union 联合体标识即共用体
union isa_t {
isa_t(){}//初始化
Class cls //绑定的类
uintptr_t bit //8字节长整型
struct //包含isa_bitfield 的结构体
}\

isa_bitfield 位域,不同系统架构对应不同的值

image.png