面试题答案
一键面试1. autoreleasepool与ARC机制的关系
- ARC(自动引用计数)是一种内存管理机制,由编译器在编译时自动插入引用计数相关的代码,如
retain
、release
和autorelease
,它大大简化了开发者手动管理内存的工作。 - autoreleasepool是一种自动释放池,当对象发送
autorelease
消息时,该对象会被添加到最近的自动释放池中。当自动释放池被销毁时,池中的所有对象都会收到release
消息。ARC环境下,虽然编译器自动管理引用计数,但autoreleasepool仍然存在并发挥作用,它决定了autorelease
对象何时被释放。
2. 显式使用autoreleasepool能显著优化性能的场景及原理
场景一:循环中创建大量临时对象
- 举例:
// 在ARC环境下
for (NSInteger i = 0; i < 1000000; i++) {
NSString *tempString = [NSString stringWithFormat:@"%ld", (long)i];
// 此处可能对tempString进行一些操作,但不持有它
}
在这个循环中,每次迭代都会创建一个新的NSString
对象。由于这些对象都被自动释放(stringWithFormat:
方法返回的对象是自动释放的),它们会在当前自动释放池销毁时才被释放。如果没有显式的自动释放池,这些对象会一直累积,直到包含该循环的自动释放池(通常是主线程的自动释放池,在每次事件循环结束时销毁)被销毁,这可能会导致内存峰值过高。
- 原理: 通过在循环内部创建一个显式的自动释放池,可以在每次迭代结束时释放这些临时对象,而不必等到外层自动释放池销毁。
for (NSInteger i = 0; i < 1000000; i++) {
@autoreleasepool {
NSString *tempString = [NSString stringWithFormat:@"%ld", (long)i];
// 此处可能对tempString进行一些操作,但不持有它
}
}
这样,每次循环结束时,@autoreleasepool
块内的对象(即tempString
)会收到release
消息,如果其引用计数降为0,就会被立即释放,从而有效降低内存峰值。
场景二:递归函数中创建大量临时对象
- 举例:
// 递归函数计算阶乘,在ARC环境下
NSInteger factorial(NSInteger number) {
if (number <= 1) {
return 1;
} else {
NSNumber *tempNumber = @(number);
NSString *tempString = [NSString stringWithFormat:@"Calculating factorial of %@", tempNumber];
// 可能有更多临时对象创建
return number * factorial(number - 1);
}
}
在递归调用过程中,会不断创建临时对象(如NSNumber
和NSString
),随着递归深度增加,内存占用会快速上升。
- 原理: 在递归函数内部添加自动释放池,每次递归调用时,创建的临时对象会在该自动释放池销毁时被释放,避免内存过度累积。
NSInteger factorial(NSInteger number) {
@autoreleasepool {
if (number <= 1) {
return 1;
} else {
NSNumber *tempNumber = @(number);
NSString *tempString = [NSString stringWithFormat:@"Calculating factorial of %@", tempNumber];
// 可能有更多临时对象创建
return number * factorial(number - 1);
}
}
}
这样可以在递归过程中及时释放不再需要的临时对象,优化内存使用,防止因内存耗尽导致程序崩溃。