栈内存分布是连续的,在创建对象开辟内存的过程中,isa是对象的第一个成员变量。
isa到底是什么
看看官方解释:
Every object is connected to the run-time system through itsisa instance variable, inherited from the NSObject class.isa identifies the object's class; it points to a structurethat's compiled from the class definition. Through isa, anobject can find whatever information it needs at run timesuch asits place in the inheritance hierarchy, the size and structure ofits instance variables, and the location of the methodimplementations it can perform in response to messages.
一个对象(Object)的isa
指向这个对象的类(Class)
,这个对象的类(Class)
的isa指向了metaclass
。这样,就可以找到相对应的静态方法和变量了。
Objective-C的运行时是动态的
,它能让你在运行的时候添加
方法或者删除
方法以及使用反射。
类的实例对象的 isa
指向它的类;
类的isa
指向该类的 metaclass
;
类的 super_class
指向期父类,如果该类为根类则值为NULL
;
metaclass的 isa
指向根 metaclass
,如果该metaclass
是根 ,metaclass
则指向自身;
metaclass
的 super_class
指向父 metaclass
,如果该 metaclass
是根 metaclass
则指向该 metaclass
对应的类;
isa的类型isa_t
isa是通过联合体(union)定义的,isa指针中通过char + 位域
(即二进制中每一位均可表示不同的信息)的原理实现。通常来说,isa
指针占用的内存大小是8字节
,即64位
,已经足够存储很多的信息了,这样可以极大的节省内存
,以提高性能。底层代码如下:
union isa_t { //联合体
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
//提供了cls 和 bits ,两者是互斥关系
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
![](https://img.haomeiwen.com/i4127520/49b0a0dbf3830cc1.png)
从isa_t的定义中可以看出:
提供了两个成员,cls
和 bits
,由联合体的定义所知,这两个成员是互斥的,也就意味着,当初始化isa指针时,有两种初始化方式
-
通过
cls
初始化,bits
无默认值 -
通过
bits
初始化,cls
有默认值 -
nonpointer
有两个值,表示自定义的类等,占1位 -
0
:纯isa指针 -
1
:不只是类对象地址,isa中包含了类信息、对象的引用计数等 -
has_assoc
表示关联对象标志位,占1位-
0
:没有关联对象 -
1
:存在关联对象
-
-
has_cxx_dtor
表示该对象是否有C++/OC的析构器
(类似于dealloc),占1位- 如果有
析构函数
,则需要做析构逻辑
- 如果
没有
,则可以更快的释放对象
- 如果有
-
shiftclx
表示存储类的指针的值(类的地址), 即类信息-
arm64
中占33
位,开启指针优化的情况下,在arm64
架构中有33
位用来存储类指针 -
x86_64
中占44
位
-
-
magic
用于调试器判断当前对象是真的对象 还是 没有初始化的空间,占6
位
weakly_refrenced
是 指对象是否被指向 或者 曾经指向一个ARC的弱变量
-
没有弱引用的对象可以更快释放
-
deallocating
标志对象是是否正在释放内存 -
has_sidetable_rc
表示 当对象引用计数大于10时,则需要借用该变量存储进位 -
extra_rc
(额外的引用计数) --- 导尿管表示该对象的引用计数值,实际上是引用计数值减1- 如果对象的引用计数为
10
,那么extra_rc
为9
- 如果对象的引用计数为
针对两种不同平台,其isa
的存储情况如图所示
![](https://img.haomeiwen.com/i4127520/7d817896fee3fc7e.png)
![](https://img.haomeiwen.com/i4127520/5e87fe380507b1b1.png)
1、2
:这是内存地址,每8
个字节会有个地址3
:对象的isa,用于查找对象的属性和方法
4、5、6
:属性的16进制表示6
:为空,原因是类里面有未赋值的属性x/4gx
:将内存地址转换成16进制。4表示打印4条数据*尽量减少无用属性,减少内存空间的浪费
获取内存大小的方式
- sizeof
- class_getInstanceSize
- malloc_size
sizeof
1、sizeof
是一个操作符
,不是函数
2、我们一般用sizeof
计算内存大小时,传入的主要对象是数据类型
,这个在编译器的编译阶段(即编译时
)就会确定大小而不是在运行时确定。
3、sizeof
最终得到的结果是该数据类型占用空间的大小
class_getInstanceSize
runtime
提供的api,用于获取类的实例对象所占用的内存大小
,并返回具体的字节数,其本质就是获取实例对象中成员变量的内存大小
malloc_size
这个函数是获取系统实际分配的内存大小
总结
-
sizeof
:计算对象类型占用的内存大小
,而对象类型中一般都有一个isa
属性,一个isa
指针所占用的字节数为8
字节,所以对象类型占用的字节数为8字节 -
class_getInstanceSize
:计算对象实际占用的内存大小
,这个需要依据类的属性而变化
,如果自定义类没有自定义属性,仅仅只是继承自NSObject,则类的实例对象实际占用的内存大小是8,可以简单理解为8字节对齐
-
malloc_size
:计算对象实际分配的内存大小
,系统分配内存是按照16字节对齐
,这个是由系统完成的,可以从上面的打印结果看出,实际分配的和实际占用的内存大小并不相等,16字节对齐
算法来解释这个问题
16字节对齐算法
//16字节对齐算法
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
![](https://img.haomeiwen.com/i4127520/83bd1171d49f328f.png)
首先将原始的内存8
与size_t(15)
相加,得到 8 + 15 = 23
将 size_t(15)
即 15进行~(取反)
操作,~(取反
)的规则是:1变为0,0变为1
最后将 23 与 15的取反结果 进行 &(与)
操作,&(与)
的规则是:都是1为1,反之为0,最后的结果为 16
,即内存的大小是以16的倍数增加的
网友评论