探寻原因
一个NSObject对象占用多少内存?
对象的isa指针指向哪里?
OC的类信息存放在哪里?
转换指令:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
揭开面纱
问题1:一个NSObject对象占用多少内存?
证明例子1:
对象的占用内存图从上面的打印结果很奇怪,发现obj指针占有的内存是16个字节,然后class_getInstanceSize获取到的对象内存却只有8个字节,另外,从main-arm64.cpp源码里面,也可以看到structNSObject_IMPL {
Class isa;
}; 按道理指针占有8个字节,答案应该是8才对,为什么会有16出现呢?进入源码里面可以发现
alloc 分配内存图证明例子2:
源码例子图:
Student 测试代码图例子终端输出图:
例子输出图这两个图得出两个结论:
第一:就是对于小于16的size,直接赋予16,否则就根据实际来分配内存,至于第二个例子为什么[Student class] 也是占用16个内存呢,是因为Student对象中还有两个成员变量,然后变量是int类型,int又占用4个字节,所以加起来应该就是8+4+4 = 16;
第二:就是structStudent_IMPL {
// struct NSObject_IMPL NSObject_IVARS; //1
Class isa; //2
int_no;
int_age;
};
1和2是对等的
下面的两张内存分布图可以说明这点,这里不做文字说明
对象内存分布图1 对象内存分布图2另外查看两个成员变量的值,也可以通过xcode的内存来看:
xcode内存图这个图也说明了,isa指针占用8个字节,_age,_no 各占4个
另外,还可以在这里通过lldb相关指令更改内存,例如现在我想将04 -->8
原始:
(lldb) x 0x0000000103402a80
0x103402a80: d1 11 00 00 01 80 1d 00 04 00 00 00 05 00 00 00 ................
0x103402a90: c1 c6 03 8f ff ff 1d 00 90 07 00 00 01 00 00 00 ................
操作内存
对内存的操作,无非就是读写操作。 修改内存中的值:
memory write 内存地址 数值
如:memory write 0x103402a89 08
读取内存操作:
memory read/数量 _ 格式 _ 字节数 内存地址 或者 x/数量 _ 格式 _ 字节数 内存地址
更改查看:
(lldb) x 0x0000000103402a80
0x103402a80: 08 11 00 00 01 80 1d 00 08 00 00 00 05 00 00 00 ................
0x103402a90: c1 c6 03 8f ff ff 1d 00 90 07 00 00 01 00 00 00 ................
可以看到已经更改成功了
证明例子3:
源码图:
源码图 源码测试图终端输出图:
终端输出图结果分析图:
源码结构图 NSObject对象分布图 Person对象分布图 Student 内存分布图从上面的图片展示,可以看到,对象的内存实际使用和分配内存都是遵循内存对齐的原则,也就是使用或者分配的内存大小必须是最大成员大小的倍数,例如Person对象分配16个对象,isa 占用8个指针,_age1 占用4个,按道理实际占用应该是12个,但是结果确显示了16个,说明使用的也是根据内存对齐的原则,可以从源码中出发探究的:
[Student alloc]-->[NSObject alloc]-->_objc_rootAlloc(Class cls)-->class_createInstance-->instanceSize-->alignedInstanceSize-->unalignedInstanceSize()--> return data()->ro->instanceSize-->word_align()
最后可以看到,对象的使用内存也是按照内存对齐法则来的
实际内存 内存对齐之后的实际内存问题2:对象的isa指针指向哪里?
1.instance对象的isa指针指向class对象
2.class对象的isa指向meta-class对象
3.meta-class对象的isa指向基类的meta-class对象
一张图说明任何事:
isa指向图证明例子:
对象的isa指向类对象 对象isa指针打印图 类对象isa指向元类图 类对象isa打印图 对象的superclass打印图证明方法的寻找路径:
对象本身有方法图 对象本身没有方法找父类方法图这个证明了当对象自身没有方法的时候会去查找父类中是否还有方法
类方法转对象方法图在这里想补充一点的就是RootMetaClass 的superclass指针这个非常特殊,因为假如我调用的是一个类方法的时候,最后一直到RootMetaClass的superclass的时候,找的是对象方法;
问题3:OC的类信息存放在哪里?
1.对象方法、属性、成员变量、协议信息,存放在class对象中
每个类在内存中有且只有一个class对象所以class一般会存放一些一次性的的内容,例如属性信息,对象方法,协议,成员变量等,进入objc的源码中可以看到的确如下:
struct objc_class :objc_object{
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_tbits; // class_rw_t * plus custom rr/alloc flags
class_rw_t*data() {
return bits.data();
}
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_tflags;
uint32_tversion;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
2.类方法,存放在meta-class对象中
3.成员变量的具体值,存放在instance对象,因为每个instance对象都拥有自己的变量
demo 测试图从编译出来的C++源码和打印的对象地址值,可以证明上述观点3,因此object1,object2在内存中的体现形式应该就是如下:
instance 分布图模拟源码测试分析结果:
实例对象存储属性和变量图 类方法存储图 元类对象存储图常用指令:
将Objective-C代码转换成C/C++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
如果需要链接其他框架的话,需要使用-framework参数。比如-framework UIKit
LLDB 指令
print,p :打印
po:打印对象
读取内存:
memory read/数量格式字节数 内存地址
x/数量格式字节数 内存地址
数量:1,2,3........
格式:x16进制,f是浮点,d是10进制
字节数:b:byte 1 字节;h:half word 2字节;w:word 4字节;g:giant word 8字节
修改内存中的值
memory write 内存地址 数值
memory write 0x00000010 10
学习总结:
1.我们平时编写的Objective-C 代码,底层实现其实都是C、C++代码,然后再转换到汇编-->机器语言的
2.Objective-C的面向对象都是基于C、C++的数据结构实现的
3.Objective-C的对象或者类主要是基于C,C++的结构体数据结构实现的
4.class_getInstanceSize([NSObject class]) 代表创建一个实例对象至少需要多少内存,遵循内存对齐原则,malloc_size(__bridge const void *)obj)代表创建一个实例对象,实际上分配了多少内存?
参考链接:
可以添加微信一起交流学习:fslskz
网友评论