简析OC中对象占用内存的原理
OC
即Objective-C
, 是iOS
的开发语言之一, 其底层都是通过C/C++
实现OC
代码在编译过程中都会被转成C/C++
代码, 之后再转成汇编语言, 最后才转成机器语言(0和1)- 所以
OC
的面向对象都是基于C/C++
的数据结构实现的, 具体一点就是通过机构提实现的(这个后面会详解)
C/C++
代码转换
- 上面说到,
OC
代码在编译过程中都会转成C/C++
代码, 不过这个过程我们看不到, 也拿不到转化后的C/C++
代码 - 所以下面我们手动将
OC
代码转成C/C++
代码 - 使用下面的命令行
1 | xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件 |
其中
cpp
就是指c plus plus
, 也就是c++
的意思
OC对象的本质
在说OC
对象的本质之前, 我们可以先点进去看一下NSObject
对象的底层定义, 如下
1 | @interface NSObject <NSObject> { |
- 抛开定义的宏不考虑, 其实就只有一个
Class
类型的isa
属性 - 下面从上一步中生成的
main.cpp
文件中, 看一下NSObject
在内存中是如何声明的
1 | struct NSObject_IMPL { |
NSObject
最后转成了NSObject_IMPL
(结构体), 我们的OC
对象最后其实就是转成了结构体; 也就是说,c++
的结构体支撑了OC
语法中的面相对象
至于isa
是什么? 我们可以点进去看一下, 其实也就是一个指向结构体struct objc_class
的指针, 那么他在64位的环境下占8个字节,在32环境上占4个字节
1 | typedef struct objc_class *Class; |
方法底层
- 现在我们在回到最初的问题, 一个
NSObject
对象占用多少内存? - 上面说到一个
isa
就需要占用8个字节, 那么一个NSObject
对象就至少需要8个字节 - 但是系统真的就为一个
NSObject
对象分配了8个字节的存储空间吗?? - 下面我们通过两个方法来计算对象所占用内存大小
1 | // runtime方法 |
从上面的代码可以看出两个方法打印出来的结果是不一样的, 但是为什么不一样呢, 方法内部优势如何实现的呢??下面我们就具体来看一下
class_getInstanceSize
class_getInstanceSize
是runtime
中的方法, 用于获取NSObject
类的示例对象的大小- 使用时需要导入对应的头文件
#import <objc/runtime.h>
- 至于其方法内部是如何实现的, 这里我们可以查看他的源码, OC所有开发的源码可以到这里查找: https://opensource.apple.com/tarballs
- 打开网页, 搜索并找到
objc4
的文件夹
打开objc4
所在文件夹, 找到最新版本的代码(数字越大版本越薪)下载, 下载完成,打开项目,然后找到class_getInstanceSize
方法的的实现(.mm
文件中)
接下来, 我们就具体看下方法内部到底是如何实现的
1 | // 方法的实现 |
- 从上面代码中
alignedInstanceSize
方法的注释中就可明白, 该方法返回的是当前类(NSObject
)的成员变量的大小 - 当前
NSObject
中就只有一个isa
指针, 所以返回8个字节 - 上面方法中提到了一个
word_align
, 可以理解为内存对齐, 后面对具体详解
malloc_size
malloc_size
是malloc
中的方法, 用于获取obj
指针所指向的内存的大小, 即系统实际分配的内存大小(最少为16)- 使用时需要导入对应的头文件
#import <malloc/malloc.h>
- 同样根据上面的方式下载源码, 找到
libmalloc
malloc_size
方法的实现代码如下
1 | size_t |