对象底层探索
- 影响对象内存的因素
- 对象内存的分布
- 联合体、位域
- nonPointerisa
- 如何利用isa的位域运算得到类对象
- new 方法
影响对象内存的因素
首先介绍打印指令
p 和 po
p是LGPerson的一个对象,通过截图可以看出不一样
想打印10进制的是 p/x ,
8进制的是p/o
2进制的是p/t
打印浮点的是p/f
然后介绍一个x指令,它能打印出对象地址
其中x/4gx :以16进制单位去打印后面的对象的4个8字节地址
x/6gx:以16进制单位去打印后面的对象的6个8字节地址
然后看一段代码:
LGPerson * p = [LGPerson new];
p.name = @"JJ"; //8 0-7
p.hobby = @"girl": //8 8-15
p.hight = 1.8.0 //8 16-23
p.age = 22;//4 24-27
p.number = 111;//4 28-31
p.a = 5;//32-33 + isa 8 34-41 因为要为16的整数倍所以是48
NSLog (@"%lu",malloc_size((__bridge const void *)(p)));
创建这么一个对象p ,通过给p的属性赋值,通过计算我们可以看到系统给分配的内存是48 ,name和hobby是NSString 8字节 *2 ,hight是long 8字节,age和number 是int4字节,a是short 2字节,每隔对象都有一个isa 8字节。可以计算得到系统会给其分配48字节(具体规则可以查看上一个篇章)。
然后打印对象p
系统在分配内存的时候是遵循小端原则的,而且会把加起来不足8字节的属性放到一起。所以平时写代码的时候 对于属性的顺序并不需要过多的在意因为在编译时候系统会帮我们去优化好。但是这里有一块要注意,由于子类没有办法修改父类的内存空间,所以在有继承我们自己创建的基类的时候还是需要我们自己去控制好父类的内存空间。
联合体和位域
struct struct1 {
char a ;
char b ;
char c ;
char d ;
}struct1 // ===> 4字节
struct struct2 {
char a :1;//位域
char b :1;
char c :1;
char d :1;
}struct2 // ===> 1字节
像struct2 的表达方式就是位域 ,有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示 注意位域的位数不可以超过数据类型的最大长度,像char a:1 ,位域就不可以超过1字节,其中1字节最多可以分成8位。:0表示不使用。
联合体:
开辟一块公用内存空间,读取的时候只显示读取类型的值,其他类型的值显示为乱码或者不显示。
union struct3{
char *name;
int age;
double height;
}struct3
1.联合体必须能够容纳最大的成员变量
2.通过计算的大小必须是最大的成员变量的整数倍
结构体和联合体的区别在于,结构体里面的成员变量可以共存,但是内存开辟比联合体多;联合体的成员变量是互斥的,能节省一定内存空间;
nonPointer_Isa
nonPointerIsa : 特殊指针
8(字节)* 8 = 64(位)
64位存储
(oc底层c++释放,如果c++没有释放,就不是释放)
小端模式 所以从右往左读(存)ULL(无符号长整型)
64位的字节里面除了储存类对象的内存对象地址,还有引用计数 还有其他
isa使用联合体就是为了兼容以前的版本
nonpointer:表示是否对isa指针开启指针优化
0:纯isa指针
1:不止是类对象地址,isa包含了类消息、对象的引用计数器等
has_assoc:关联对象标志位,0没有1存在
has_cxx_dtor:该对象是否有C++或者Objc的析构器,如果有析构器则需要析构逻辑,没有则可以更快释放对象
shiftcls:存储类指针的值。开启指针优化下,在arm64架构中有33位来储存类指针
magic:用于调试器判断当前对象真的对象还是没有初始化的空间
weakly_referenced:标志对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用对象可以更快的释放
deallocating:标志对象是否正在释放内存
has_sidetable_rc:当对象引用计数大于extra_rc所能储存的最大范围时,需要借用该变量储存进位
extra_rc:当表示该对象的引用计数值,当对象的因技术超出最大的储存范围时,则需要使用上面的has_sidetable_rc
如何利用isa的位域运算得到类对象
uintptr_t shiftcls , 打比方当前uintptr_t shiftcls为33 ,isa总长64位 中间的这33位就是类对象的地址,如果计算出来呢
new 方法
在源码中可以看到
所以new 其实就是alloc + init方法