面试题答案
一键面试手动引用计数(MRC)和自动引用计数(ARC)的区别
- 内存管理方式:
- MRC:开发者需要手动调用
retain
、release
和autorelease
等方法来管理对象的引用计数。例如,当创建一个对象后,若要持有该对象,需调用retain
方法使引用计数加1;当不再需要该对象时,调用release
方法使引用计数减1,当引用计数为0时,对象被释放。 - ARC:编译器自动在适当的位置插入
retain
、release
和autorelease
方法。开发者无需手动调用这些方法,大大减轻了内存管理的负担,降低了因手动管理不当导致的内存泄漏和悬空指针等问题。
- MRC:开发者需要手动调用
- 代码复杂度:
- MRC:代码中充斥着大量的引用计数操作代码,使得代码可读性和维护性变差,尤其是在复杂的对象关系和内存管理逻辑中。
- ARC:代码简洁,开发者可以更专注于业务逻辑的实现,无需花费大量精力在繁琐的内存管理上。
- 内存泄漏风险:
- MRC:由于手动管理,很容易出现忘记调用
release
方法或者调用次数不当的情况,从而导致内存泄漏。 - ARC:通过编译器自动管理引用计数,有效避免了大部分因手动管理失误造成的内存泄漏问题。
- MRC:由于手动管理,很容易出现忘记调用
ARC环境下自动释放池(@autoreleasepool)的工作原理
- 自动释放对象的存储:当一个对象发送
autorelease
消息时,它会被添加到最近的自动释放池中。自动释放池实际上是一个栈结构,对象以栈的方式被压入池中。 - 对象释放时机:当自动释放池被销毁时,它会向池中的所有对象发送
release
消息。如果某个对象的引用计数在接收到release
消息后变为0,则该对象被释放。在 iOS 应用程序中,主线程的自动释放池会在每次事件循环结束时被销毁并重新创建,这确保了在事件处理过程中自动释放的对象能够及时得到释放。
实际项目中需要手动创建自动释放池的场景
- 大量临时对象创建:在循环中创建大量临时对象时,如果不手动创建自动释放池,这些对象会一直累积在自动释放池中,直到当前自动释放池销毁(例如主线程事件循环结束)才会被释放,可能会导致内存峰值过高。例如:
for (int i = 0; i < 1000000; i++) {
@autoreleasepool {
NSString *tempString = [NSString stringWithFormat:@"%d", i];
// 其他操作
}
}
在上述代码中,每次循环创建的 tempString
对象在 @autoreleasepool
块结束时就会被释放,而不会等到主线程的自动释放池销毁时才释放,从而有效控制内存峰值。
2. 后台任务:在后台线程中,如果有大量对象创建和自动释放操作,手动创建自动释放池可以更好地管理内存。因为后台线程没有像主线程那样默认的自动释放池管理机制,若不手动创建,可能会导致内存持续增长。例如在 NSThread
或 GCD
的后台任务中:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@autoreleasepool {
// 执行大量对象创建和释放的操作
}
});
通过手动创建自动释放池,可以确保在后台任务执行过程中及时释放不再需要的对象,避免内存问题。