面试题答案
一键面试1. 类与对象的内部结构
在Objective-C运行时机制中,对象本质上是一个结构体指针。以NSObject
为例,其底层定义大致如下:
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
这里isa
指针指向对象所属的类。而类Class
本身也是一个结构体,包含了类的元数据,例如:
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // 方法缓存
class_data_bits_t bits; // 类的其他数据,包含属性、方法列表等
};
2. 对象在内存中的对齐规则
- 基本数据类型对齐:内存对齐的基本规则是每个数据成员存储的起始地址必须是该数据成员大小的整数倍。例如,
char
类型大小为1字节,它可以从任何地址开始存储;int
类型在32位系统中通常为4字节,它的起始地址必须是4的倍数。 - 结构体对齐:对于结构体,结构体的大小必须是其最大成员大小的整数倍。同时,结构体内部成员的存储顺序也会影响结构体的大小。例如:
struct {
char a; // 1字节
int b; // 4字节,由于对齐,a后填充3字节,b从4的倍数地址开始
} Struct1; // 整体大小为8字节
struct {
int b; // 4字节
char a; // 1字节,b后填充3字节,a从4的倍数地址开始
} Struct2; // 整体大小为8字节
- 对象对齐:Objective-C对象同样遵循上述规则。由于对象本质是结构体,且第一个成员是
isa
指针(通常在64位系统中为8字节),所以对象的起始地址必须是8的倍数。对象中的其他属性按照类型大小和对齐规则依次排列。
3. 对齐方式对类的布局影响
- 属性排列:类中的属性按照声明顺序和对齐规则在内存中排列。如果属性类型大小不同,为了满足对齐要求,可能会在属性之间产生填充字节。例如:
@interface MyClass : NSObject {
char a;
int b;
double c;
}
@end
a
占用1字节,为了让b
(4字节)从4的倍数地址开始,a
后填充3字节;b
占用4字节;c
(8字节)为了从8的倍数地址开始,b
后填充4字节。整个对象大小为1 + 3 + 4 + 4 + 8 = 20
字节,但由于对象整体需对齐到8的倍数,所以最终对象大小为24字节。
- 内存浪费与优化:对齐规则可能导致一定的内存浪费,特别是当类中有许多小数据类型属性时。可以通过合理调整属性声明顺序,尽量减少填充字节,优化内存使用。例如将大字节属性放在前面,小字节属性放在后面。
4. 对齐方式对方法调用效率的影响
- 方法缓存:类的
cache_t
缓存方法调用,其存储结构与内存对齐也有关系。由于缓存的查找通常基于内存地址的快速计算,如果对象和缓存结构都遵循良好的对齐规则,能更高效地定位缓存中的方法,提高方法调用效率。 - 内存访问效率:当方法访问对象的属性时,对齐良好的内存布局能使CPU更高效地读取数据。现代CPU通常以块(如64位系统中8字节块)为单位读取内存,如果属性地址对齐,一次内存读取操作就能获取完整的数据,减少了内存访问次数,提高方法执行效率。