在 Objective-C 中,一个对象在内存中的最小结构本质上就是一个 C 语言结构体。它的“极简版”只包含一个成员,即指向类信息的指针。
1. 最小结构的定义
在 Runtime 源码(objc.h)中,对象的最小定义是 objc_object 结构体:
C
struct objc_object {
Class _Nonnull isa;
};
isa指针: 它是对象的“身份证明”。在 64 位架构下,这个指针占 8 字节。- 含义: 只要这 8 字节存在,Runtime 就能通过它找到该对象所属的 类(Class) ,进而知道它能响应哪些消息。
2. 实际占用的内存:16 字节
虽然结构体理论上只需要 8 字节,但在实际分配中,Objective-C 对象的最小内存占用是 16 字节。
这是由两个层面的限制决定的:
- Runtime 限制: 在
allocWithZone:底层实现中,系统硬编码了对象分配至少为 16 字节。 - 内存对齐(Memory Alignment): 为了提高 CPU 读取效率,iOS/macOS 的堆内存分配通常以 16 字节为对齐准则。
3. 对象结构的“进化”:Non-pointer isa
在现代 64 位架构(如 iOS 的 ARM64)中,isa 不再是一个简单的纯指针,而是一个 isa_t 共用体(union) 。
为了不浪费这 8 字节(64 位)的巨大空间,苹果在其中塞入了大量额外信息,这就是所谓的 Tagged Pointer 思想的延伸:
- indexed (1 bit): 0 代表纯指针,1 代表里面包含了额外信息。
- has_assoc (1 bit): 对象是否有关联对象。
- has_sidetable_rc (1 bit): 引用计数是否过大,需要存到外部哈希表中。
- shiftcls (33+ bits): 真正的类地址偏移量。
- extra_rc: 存储对象的引用计数(减 1 后的值)。
这意味着: 一个最简单的对象,其内存不仅存储了“我是谁”,还顺便存储了它的“引用计数”和“是否被弱引用”等状态。
4. 完整的内存布局示意
当你给对象增加成员变量(ivar)时,它们会按顺序排在 isa 指针之后:
| 偏移量 (Offset) | 内容 | 说明 |
|---|---|---|
| 0 ~ 7 字节 | isa | 存储类地址及状态位(必须有) |
| 8 ~ 15 字节 | ivar1 | 用户定义的第一个变量(如 int) |
| 16 ~ 23 字节 | ivar2 | 第二个变量(按对齐补齐) |
总结
Objective-C 对象的逻辑最小结构是一个 isa 指针,占用 8 字节;但在 物理内存 中,任何一个实例对象至少占用 16 字节。