题记
这系列的目的是回顾一下学到的知识,对OC底层原理作一翻探究,以及对一些常见的底层原理面试题作一次总结。
准备工作
-
如题目所示,我们新建一个demo来探究:一个NSObject对象占多大内存
如大部分iOS开发者所知,OC的底层是C/C++,为了清楚了解NSObject的本质,我们可以看看底层是怎么实现。
- 进入mian.m文件所在的路径,打开terminal
- 输入以下指令把OC代码转成C++(因为mac平台下使用的是arm64架构,实际情况可以选择相应的架构)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
-
我们可以得到一个main-arm64.cpp 文件
- 把cpp文件直接拽入Xcode,为了不显示报错信息,我们不让它参与编译
探究
-
点击NSObject我们可以看到OC的定义
-
在main.cpp文件里面搜索NSObject_IMPL,我们可以看到NSObject转成C++后的本质是一个结构体
- 我们再点击Class进去,可以看到其实它就是一个指针。那么我们知道在64位环境下,它占8个字节。
论证
- 有了这个推论,我们还可以进一步论证我们的观点,这里我们用到runtime,也就是运行时的一个方法class_getInstanceSize
- 为了验证推论的准确性,我们先找到苹果开源的源码,打开浏览器
http://opensource.apple.com/tarballs/
搜索`objc
-
点击进去,我们找到数字最大的一个,一般来说也是最新的一个
-
下载压缩包解压后并打开
- 搜索
class_getInstanceSize
,并在.mm文件里面可以看到它的实现
-
点击3中的alignedInstanceSize()方法,我们可以看到进一步注释,这个方法返回的是类对象成员变量所占用的内存大小
- 所以通过调用runtime的这个方法我们可以看到,NSObject类对象所占用的内存大小为8个字节
疑问
- 这个就是最终结果了吗?不用急着下定义。我们还有个叫malloc_size的方法可以查看系统分配内存的大小。由于这里需要传一个C的指针,所以我们打印obj的时候需要桥接一下。然后我们会发现,返回的大小是16而不是8!
总结
-
为什么会造成这个不一致呢?我们还是要从源码入手分析,首先还是在objc的源码里面搜索allocWithZone这个方法,查看创建一个对象是内存是怎么分配
- 我们点击进入
class_createInstance
这个方法,看到返回的是_class_createInstanceFromZone
方法,我们再次点击进去就能很快锁定我们需要的信息:size
-
我们可以从注释看到,所有对象至少分配16 bytes 的大小,从方法实现来看,如果size < 16,则size = 16