写在前面:
- 在了解内存对齐之前先了解一下各数据类型在内存中的大小,目前我们比较常用的是
64位系统,所以我们的研究对象统一采用64位的大小作为参考。
image.png
了解下lldb调试命令:
po 打印信息
p 打印详细的信息
bt 打出堆
register read 读取寄存器
x 读取内存段
x/4gx 读取4段内存段
说到内存对齐这个玩意儿,说实话一脸懵逼!
光这个三个玩意儿(如何获取内存的大小)够喝一壶:
1.sizeof()
2.class_getInstanceSize()
3.malloc_size()
一块看看是个啥吧,创建一个类,属性如下
@interface PHPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@end
- 导入头文件
· #import <objc/runtime.h>
· #import <malloc/malloc.h>
-
打印结果
打印结果
分配情况一
我的理解是(个人理解有待验证):
sizeof:是person这个指针所isa需要的内存大小8字节固定
class_getInstanceSize:存储这个PHPerson类所需要的空间大小
malloc_size:系统分配的空间大小(内存对齐原则16字节)
为了验证咱的猜测,添加断点
-
Xcode查看内存地址:Debug->Debug Workflow->view memory
image.png
iOS是小端模式,需要倒着读数据.(详解大端模式和小端模式)
- 大端模式:是指数据的高字节保存在内存的低地址中,而低子节数据保存在内存的高地址中。
- 小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
注释掉heigh类型为long的属性之后打印如图:image.png
分配情况二
注释掉所有属性之后
image
分配情况三
属性占用字节数
这下懵了:
- 对象申请的内存和系统开辟的内存 有什么不一样?
- 不是说对象最少为16字节,为什么class_getInstanceSize还能等于8?
通过上篇OC对象之alloc探索-源码探索的三种方式中定位instanceSize
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个字节的空间(可以通过class_getInstanceSize函数获得)
class_getInstanceSize(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
#ifdef __LP64__
# define WORD_SHIFT 3UL
# define WORD_MASK 7UL
# define WORD_BITS 64
#else
# define WORD_SHIFT 2UL
# define WORD_MASK 3UL
# define WORD_BITS 32
#endif
static inline uint32_t word_align(uint32_t x) {
//此时WORD_MASK是64位下的WORD_MASK 7UL 即8字节对齐
return (x + WORD_MASK) & ~WORD_MASK;
}
通过一个例子先了解下字节对齐,下边再探
struct object {
int a; // 4
NSString *b; // 8
int c; // 4
char d; // 1
};
- 未对齐
- 对齐
![]()
打开PHPerson.h中所有属性变为成员变量方便分析,此时cd到当前工程目录下通过xcrun -sdk iphonesimulator clang -rewrite-objc main.m讲main.m编译成cpp的c++文件如图
image.png
@interface PHPerson : NSObject{
NSString *name;
NSString *nickName;
int age;
long height;
}
编译后可以看到PHPerson对象其实是个结构体指针,内存对齐可以参考上方结构体object
struct PHPerson_IMPL {
//指针对象,占用8字节
struct NSObject_IMPL NSObject_IVARS;
NSString *name;//字节8 0-7
NSString *nickName;//8字节 8-15
int age;//4字节 16-20 补齐21-23
long height;//8字节 24-31
}; 共需要8+32 字节数
-
发现
PHPerson结构体中自动增加了一个成员变量NSObject_IVARS,即Class类型的成员变量。于是,获取PHPerson实例对象的内存大小,就等价于获取PHPersonl_IMPL结构体所占用的内存大小。 -
在上述的讲解中,可以知道获取内存大小方法的区别。
malloc_size函数获取系统分配的内存大小,class_getInstanceSize是获取实际占用的内存大小。 -
通过上述打印结果可以看出,系统分配给
PHPerson实例对象内存是48个字节,实际占用的也是40个字节。看到这,想必疑问重重,实际占用的内存大小不应该是8 + 8 + 4 + 8 = 28个字节么?这是怎么回事呢? -
其实这里涉及一点计算机的知识点——内存对齐。在结构体中,总大小为结构体对最大成员大小的整数倍,如不满足,最后填充字节以满足,可分配的最小内存是结构体中内存占用最大的成员变量的大小。
-
在
PHPerson_IMPL结构体中,占用内存最大的成员变量是NSObject_IVARS,name,nickName,height,占用的内存是32个字节。成员变量_age占用4个字节,由于不满足内存对齐规则,故实际占用8个字节,实际占用的内存大小就是40个字节。

image.png
分配情况二
- 对齐








网友评论