NSObject的定义
NSObject 是OC的基础对象类型,是通过C/C++的结构体进行面向对象封装而成的,在runtime
的源码里,是对 Class
类型的 isa
的一个封装。
Class
其实是结构体类型 objc_class
的指针。
isa
isa
在arm64架构之前,就是一个普通的指针,存储着 类Class
或者 元类meta-Class
对象的内存地址;但是从arm64之后,对isa进行了优化,变成了一个 union
共用体结构,与 struct
结构相似,但是 union
中多个成员共用一块内存,占用内存是是成员中长度最长的类型需要的内存大小。
ISA_BITFIELD
中,
- 1、nonpointer:0,代表普通的指针,存储着Class、Meta-Class对象的内存地址;1,代表优化过,使用位域存储更多的信息
- 2、has_assoc:是否有设置过关联对象,如果没有,释放时会更快
- 3、has_cxx_dtor:是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
- 4、shiftcls:存储着Class、Meta-Class对象的内存地址信息
- 5、magic:用于在调试时分辨对象是否未完成初始化
- 6、weakly_referenced:是否有被弱引用指向过,如果没有,释放时会更快
- 7、deallocating:对象是否正在释放
- 8、extra_rc:里面存储的值是引用计数器
- 9、has_sidetable_rc:引用计数器是否过大无法存储在isa中;如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
各个字段后面的数字,表示字段占用的位(bit)个数,总长度还是 64位(8字节),依旧是一个指针的大小。变成 union
结构之后,isa 指针存放的信息更多更节省内存了。
NSOBject 对象的内存大小
查看对象的内存大小,我们一般通过两个API
//runtime.h 实际使用的内存大小
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
//输出 8
//malloc.h 实际分配的内存大小
NSLog(@"%zd", malloc_size((__bridge void *)obj));
//输出 16
在源码里面追踪 class_getInstanceSize
,得到的说明是返回的参数类的成员变量大小,向上取整到指针大小的界限。
创建对象时候分配内存我们都是使用的 alloc
方法,在源码里,方法的调用顺序是这样的(allocWithZone
也类似,最终落点是一样的)
最后落于 _class_createInstanceFromZone
这个方法
malloc_zone_calloc
或者 calloc
分配内存,而大小都是使用的 size_t size = cls->instanceSize(extraBytes);
这样一个方法来获取的大小,这个就是核心,那看看核心代码
可以看到分配内存的时候,分配的大小就是 alignedInstanceSize
+ 额外传入的大小,而且当size小于16个字节时自动扩展到16个字节。
如此,得到两点信息
class_getInstanceSize
方法返回的是对象实际需要占用的内存大小,结构体内存对齐(最大数据类型为isa指针),大小为8字节(指针大小)的倍数。malloc_size
方法返回的是系统给对象分配的内存大小,对齐策略不一样,大小为16字节的倍数。
OC对象的分类
OC 的对象,主要分为三种
- instance 实例对象,由类
alloc
出来的对象,每次调用alloc
都会产生一个新的实例对象。 - class 类对象,每个类在内存中有且只有一个类对象。
- meta-class 元类对象,每个类的内存中有且只有一个元类对象。
- isa指针
- 其他成员变量(具体值)
类对象 存储
- isa指针
- superClass指针
- 成员变量(成员变量描述性信息,比如变量类型之类,不是具体值)
- 类的对象方法(
-
实例方法) - 类的协议信息(protocol)
- 类的属性信息(@property)
元类对象 存储
- isa指针
- superclass指针
- 类方法(
+
方法)
三种类型的关系图总结