面试题答案
一键面试1. 快速枚举底层实现原理
- 协议:快速枚举基于
NSFastEnumeration
协议。该协议定义了一个方法- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id [])stackbuf count:(NSUInteger)len
。 - 方法:通过这个方法,对象可以返回其包含的对象数量,并将对象填充到给定的缓冲区中。
NSFastEnumerationState
结构体用于维护枚举状态,包括当前索引、是否已完成等信息。 - runtime机制:在运行时,编译器会将快速枚举的代码转化为对
NSFastEnumeration
协议方法的调用。这种机制允许集合类(如NSArray
、NSDictionary
等)提供高效的枚举实现,因为它避免了传统NSEnumerator
的开销,直接在对象内部进行快速迭代。
2. 性能优化思路
思路一:减少内存拷贝
- 原理:在快速枚举中,
stackbuf
是一个栈上的缓冲区。当数据集很大时,频繁地将对象填充到这个缓冲区可能会导致性能问题。通过使用基于堆的缓冲区,可以减少栈上的内存压力和拷贝次数。 - 实现方式:自定义一个符合
NSFastEnumeration
协议的类,在实现- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id [])stackbuf count:(NSUInteger)len
方法时,分配一个堆上的缓冲区(例如malloc
分配内存),并在枚举完成后释放它。示例代码如下:
@implementation LargeDataSet
{
NSMutableArray *data;
}
- (instancetype)initWithData:(NSMutableArray *)array {
self = [super init];
if (self) {
data = array;
}
return self;
}
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id [])stackbuf count:(NSUInteger)len {
if (state->state == 0) {
state->mutationsPtr = &data;
state->extra[0] = 0;
}
NSUInteger count = data.count - state->extra[0];
if (count > len) {
count = len;
}
for (NSUInteger i = 0; i < count; i++) {
stackbuf[i] = data[state->extra[0] + i];
}
state->extra[0] += count;
return count;
}
@end
思路二:分段枚举
- 原理:将大数据集分成多个较小的段进行枚举,每次只处理一段数据,减少一次性处理的数据量,降低内存压力。
- 实现方式:在自定义类中,记录数据集的总长度,并在
NSFastEnumeration
协议方法中根据当前枚举状态计算出当前段的起始和结束索引。例如:
@implementation LargeDataSet
{
NSMutableArray *data;
NSUInteger segmentSize;
}
- (instancetype)initWithData:(NSMutableArray *)array segmentSize:(NSUInteger)size {
self = [super init];
if (self) {
data = array;
segmentSize = size;
}
return self;
}
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id [])stackbuf count:(NSUInteger)len {
if (state->state == 0) {
state->mutationsPtr = &data;
state->extra[0] = 0;
}
NSUInteger startIndex = state->extra[0];
NSUInteger endIndex = MIN(startIndex + segmentSize, data.count);
NSUInteger count = endIndex - startIndex;
if (count > len) {
count = len;
}
for (NSUInteger i = 0; i < count; i++) {
stackbuf[i] = data[startIndex + i];
}
state->extra[0] += count;
return count;
}
@end
在使用时,可以像这样创建并枚举对象:
NSMutableArray *largeData = [NSMutableArray arrayWithCapacity:1000000];
// 填充数据
LargeDataSet *dataSet = [[LargeDataSet alloc] initWithData:largeData segmentSize:1000];
for (id obj in dataSet) {
// 处理对象
}