面试题答案
一键面试ARC(自动引用计数)管理内存方式
- 对象销毁时:ARC 会在对象的引用计数降为 0 时,自动释放该对象占用的内存。编译器会在编译期自动插入适当的内存管理代码,例如 retain、release 和 autorelease 等操作。
- 工作原理:
- 编译期处理:编译器会分析代码中对象的生命周期,在适当的位置插入内存管理的调用。当对象被创建或被赋值给一个新的变量时,引用计数增加;当对象的作用域结束或者变量被赋值为 nil 时,引用计数减少。
- 运行时机制:ARC 依赖运行时系统来跟踪对象的引用计数。运行时系统会维护一个引用计数表,记录每个对象的引用计数。当引用计数变为 0 时,运行时系统会自动调用对象的 dealloc 方法来释放内存。
MRC(手动引用计数)管理内存方式
- 对象销毁时:在 MRC 中,开发者需要手动调用 release 方法来减少对象的引用计数。当引用计数降为 0 时,对象会自动调用 dealloc 方法释放内存。如果开发者忘记调用 release 方法,就会导致内存泄漏。
ARC 环境下可能出现的内存泄漏场景及解决方案
- 循环引用:
- 场景:两个或多个对象相互强引用,导致它们的引用计数永远不会降为 0。例如,对象 A 持有对象 B 的强引用,对象 B 又持有对象 A 的强引用。
- 解决方案:使用弱引用(weak)或无主引用(unowned)来打破循环引用。在上述例子中,可以将其中一个引用改为 weak 或 unowned。如果对象 B 可能为 nil,可以使用 weak;如果对象 B 不会为 nil,可以使用 unowned。
- NSTimer 导致的泄漏:
- 场景:如果使用
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
方法创建 NSTimer,并且 target 是 self,由于 NSTimer 对 target 持有强引用,而 self 又对 NSTimer 持有强引用,就会导致循环引用。 - 解决方案:
- 使用 block 形式的 NSTimer 初始化方法
scheduledTimerWithTimeInterval:repeats:block:
,这样可以避免强引用 target。 - 或者在适当的时候手动 invalidate NSTimer,并将其设置为 nil。
- 使用 block 形式的 NSTimer 初始化方法
- 场景:如果使用
- Block 导致的泄漏:
- 场景:如果 block 内部使用了 self,并且 block 被强引用,就会形成循环引用。例如,在 viewController 中定义一个 block 属性,并在 block 内部访问 self 的属性。
- 解决方案:使用 weak 或 strong 关键字修饰 self。一般先使用
__weak typeof(self) weakSelf = self;
,然后在 block 内部使用weakSelf
。如果需要在 block 内部保持 self 的生命周期,可以在 block 内部再使用__strong typeof(weakSelf) strongSelf = weakSelf;
。