目录
- Objective-C的本质
- NSObject的底层实现
- 一个
NSObject对象
的底层内存分布情况- 内存占用查看工具
----Xcode方式查看
----控制台LLDB
命令方式查看- 拓展Student对象内存布局
- 内存对齐-继承结构
- OC内存对齐
- 其他补充
----数据类型内存占用表
----容易混淆的2个函数
----Xcode实时查看内存数据
----常用LLDB
指令
Objective-C的本质
我们平时编写的Objective-C代码底层是由C\C++实现的。OC的面向对象是基于C\C++的数据结构实现的。OC的对象、类主要基于C\C++的结构体实现的。OC代码转换过程:
NSObject的底层实现
NSObject * obj = [[NSObject alloc] init];
NSObject对象的底层实现是C++的结构体:
// OC代码
@interface NSObject {
Class isa;
}
// 底层实现
struct NSObject_IMPL {
Class isa;
};
在C++中,NSObject是一个结构体,内部只有一个
isa指针变量
。
isa指针的大小就是结构体所占内存大小,结构体的内存地址就是isa指针的地址。
在32位系统下占用4个字节
,64位系统占用8个字节
。
一个NSObject对象的底层内存分布情况
// 调用class_getInstanceSize方法需要导入头文件
#import <objc/runtime.h>
// 调用malloc_size方法需要倒入头文件
#import <malloc/malloc.h>
NSObject *obj = [[NSObject alloc] init];
// 获得NSObject实例对象的成员变量所占用的大小 -> 8
NSLog(@"obj = %zd",class_getInstanceSize([NSObject class]));
// 获得obj指针所指向内存的大小 -> 16
NSLog(@"obj = %zd",malloc_size((__bridge const void*)obj));
通过打印结果观察class_getInstanceSize
方法方法获取obj对象占8个字节
,通过malloc_size
方法获取obj对象占用16个字节
,为什么会读取出两个不同的大小?
分析 objc源码
可知class_getInstanceSize
最终调用了alignedInstanceSize
,获取的是成员变量的大小,即isa指针所占大小8
:
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
分析malloc源码
可知malloc_size
最终调用了instanceSize
,获取的是对象所占内存大小,最小为16个字节,所以得出16:
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
NSObject
对象使用了前8个字节
,后8个字节
是空的,NSObject对象数据结构如下:
内存查看工具
-
Xcode
利用Xcode可以查看对象的内存布局,Debug -> Debug Workfllow -> View Memory
,输入NSObject对象地址:
-
控制台LLDB`命令输出
也可以在控制台通过LLDB
命令进行查看,复制obj
对象的内存地址,然后在控制台输入memory read 粘贴内存地址
Printing description of obj:
<NSObject: 0x28121c000>
(lldb) memory read 0x28121c000
0x28121c000: 49 ef 4d f0 01 00 00 01 00 00 00 00 00 00 00 00 I.M.............
0x28121c010: 49 ef 4d f0 01 00 00 01 00 00 00 00 00 00 00 00 I.M.............
(lldb)
拓展Student对象内存布局
创建一个继承于NSObject
的类Student
@interface Student : NSObject {
@public
int _no;
int _age;
}
通过观察可以发现Student
继承自NSObject
,而NSObject
底层的结构体只有一个isa
,所以Student
的底层实现为:
struct NSObject_IMPL {
Class isa;
};
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _no;
int _age;
};
我们对进行Student
类进行初始化并赋值
Student *stu = [[Student alloc] init];
stu->_no = 4;
stu->_age = 5;
NSLog(@"%zd", class_getInstanceSize([Student class]));
NSLog(@"%zd", malloc_size((__bridge const void *)stu));
16
16
Student
实例对象包含父类的isa指针
和自己的成员变量,isa
占八个字节,int
声明的对象占四个字节
,所以Student
在开辟的内存空间大小就是8+4+4=16
。
我们看一下内存地址:
Printing description of stu:
<Student: 0x101012770>
(lldb) memory read 0x101012770
0x101012770: 61 81 00 00 01 00 00 01 04 00 00 00 05 00 00 00 a...............
0x101012780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
(lldb)
内存地址是按照第一个对象的地址
来的。所以可以推算出Student
在堆中内存地址分布情况
内存对齐-继承结构
创建一个Person
类继承于NSObject
,创建一个Student
类继承于Person
,分别有各自的属性
@interface Person : NSObject {
int _no;
}
@interface Student : Person {
int _age;
}
NSObject、Person、Student的底层结构如下:
struct NSObject_IMPL {
Class isa;
};
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
};
struct Student_IMPL {
struct NSObject_IMPL Person_IVARS;
int _no;
};
Person
和Student
的内存结构为:
分别打印person
和stu
对象的class_getInstanceSize
和malloc_size
NSLog(@"person = %zd",class_getInstanceSize([Person class]));
NSLog(@"person = %zd",malloc_size((__bridge const void*)person));
NSLog(@"stu = %zd",class_getInstanceSize([Student class]));
NSLog(@"stu = %zd",malloc_size((__bridge const void*)stu));
打印结果:
person = 16
person = 16
stu = 16
stu = 16
虽然person
成员变量所占实际大小是12个字节
,但是class_getInstanceSize
实际上返回的是对齐后的内存大小,所以应该是8的倍数
,返回的就是16
。
内存对齐:结构体的最终大小必须是最大成员大小的倍数
OC的内存对齐
创建一个Student类继承于NSObject
@interface Student : NSObject {
int _no;
int _age;
int _height;
}
NSLog(@"stu = %zd",class_getInstanceSize([Student class]));
NSLog(@"stu = %zd",malloc_size((__bridge const void*)stu));
打印结果:
24
32
分析:成员变量所占内存大小8 + 4 + 4 + 4 = 20
,根据内存对其规则,应该是8的倍数
,所以结果是24
,那么为什么malloc_size
读取的是32
?
class_getInstanceSize
是获取创建这个实例对象:【至少需要多少内存】。
malloc_size
是获取创建这个实例对象:【实际上分配了多少内存】。
OC内存对齐单位是16或者16的倍数
。
所以创建Student对象需要24个字节
,但是实际上分配了32个字节
。
其他补充
-
数据类型内存占用表
-
容易混淆的2个函数
创建一个实例对象,至少需要多少内存?
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);
创建一个实例对象,实际上分配了多少内存?
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);
-
实时查看内存数据
Debug -> Debug Workfllow -> View Memory
-
常用LLDB指令
print、p:打印
po:打印对象
// 读取内存
memory read/数量格式字节数 内存地址
x/数量格式字节数 内存地址
x/3xw 0x10010
网友评论