MST
星途 面试题库

面试题:Objective-C 中的内存管理与自动释放池

请阐述 Objective-C 中手动引用计数(MRC)和自动引用计数(ARC)的区别。在 ARC 环境下,自动释放池(@autoreleasepool)的工作原理是什么?在实际项目中,哪些场景下需要手动创建自动释放池?
21.3万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

手动引用计数(MRC)和自动引用计数(ARC)的区别

  1. 内存管理方式
    • MRC:开发者需要手动调用 retainreleaseautorelease 等方法来管理对象的引用计数。例如,当创建一个对象后,若要持有该对象,需调用 retain 方法使引用计数加1;当不再需要该对象时,调用 release 方法使引用计数减1,当引用计数为0时,对象被释放。
    • ARC:编译器自动在适当的位置插入 retainreleaseautorelease 方法。开发者无需手动调用这些方法,大大减轻了内存管理的负担,降低了因手动管理不当导致的内存泄漏和悬空指针等问题。
  2. 代码复杂度
    • MRC:代码中充斥着大量的引用计数操作代码,使得代码可读性和维护性变差,尤其是在复杂的对象关系和内存管理逻辑中。
    • ARC:代码简洁,开发者可以更专注于业务逻辑的实现,无需花费大量精力在繁琐的内存管理上。
  3. 内存泄漏风险
    • MRC:由于手动管理,很容易出现忘记调用 release 方法或者调用次数不当的情况,从而导致内存泄漏。
    • ARC:通过编译器自动管理引用计数,有效避免了大部分因手动管理失误造成的内存泄漏问题。

ARC环境下自动释放池(@autoreleasepool)的工作原理

  1. 自动释放对象的存储:当一个对象发送 autorelease 消息时,它会被添加到最近的自动释放池中。自动释放池实际上是一个栈结构,对象以栈的方式被压入池中。
  2. 对象释放时机:当自动释放池被销毁时,它会向池中的所有对象发送 release 消息。如果某个对象的引用计数在接收到 release 消息后变为0,则该对象被释放。在 iOS 应用程序中,主线程的自动释放池会在每次事件循环结束时被销毁并重新创建,这确保了在事件处理过程中自动释放的对象能够及时得到释放。

实际项目中需要手动创建自动释放池的场景

  1. 大量临时对象创建:在循环中创建大量临时对象时,如果不手动创建自动释放池,这些对象会一直累积在自动释放池中,直到当前自动释放池销毁(例如主线程事件循环结束)才会被释放,可能会导致内存峰值过高。例如:
for (int i = 0; i < 1000000; i++) {
    @autoreleasepool {
        NSString *tempString = [NSString stringWithFormat:@"%d", i];
        // 其他操作
    }
}

在上述代码中,每次循环创建的 tempString 对象在 @autoreleasepool 块结束时就会被释放,而不会等到主线程的自动释放池销毁时才释放,从而有效控制内存峰值。 2. 后台任务:在后台线程中,如果有大量对象创建和自动释放操作,手动创建自动释放池可以更好地管理内存。因为后台线程没有像主线程那样默认的自动释放池管理机制,若不手动创建,可能会导致内存持续增长。例如在 NSThreadGCD 的后台任务中:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @autoreleasepool {
        // 执行大量对象创建和释放的操作
    }
});

通过手动创建自动释放池,可以确保在后台任务执行过程中及时释放不再需要的对象,避免内存问题。