面试题答案
一键面试检测循环引用
- 工具检测:
- 使用 Instruments 工具中的 Leaks 模板。它可以实时监测内存泄漏,当出现循环引用导致对象无法释放时,Leaks 工具会标记出可能泄漏的对象及其相关信息,通过分析这些信息可以定位到循环引用的代码位置。例如,如果有两个自定义类
ClassA
和ClassB
相互强引用形成循环引用,Leaks 工具可能会显示这两个类的实例对象无法释放,通过堆栈信息可以追踪到创建这两个对象并建立相互引用的代码行。
- 使用 Instruments 工具中的 Leaks 模板。它可以实时监测内存泄漏,当出现循环引用导致对象无法释放时,Leaks 工具会标记出可能泄漏的对象及其相关信息,通过分析这些信息可以定位到循环引用的代码位置。例如,如果有两个自定义类
- 代码分析:
- 人工检查代码逻辑,特别是对象之间的引用关系。如果发现两个或多个对象之间存在相互强引用的情况,就可能存在循环引用风险。例如:
@interface ClassA : NSObject
@property (strong, nonatomic) ClassB *classB;
@end
@interface ClassB : NSObject
@property (strong, nonatomic) ClassA *classA;
@end
在上述代码中,ClassA
和 ClassB
通过 classB
和 classA
属性相互强引用,这就可能导致循环引用。
解决循环引用
- 弱引用(Weak References):
- 将其中一个相互引用的关系改为弱引用。例如,修改上述代码为:
@interface ClassA : NSObject
@property (strong, nonatomic) ClassB *classB;
@end
@interface ClassB : NSObject
@property (weak, nonatomic) ClassA *classA;
@end
这样,ClassB
对 ClassA
的引用是弱引用,不会增加 ClassA
的引用计数。当 ClassA
的其他强引用都被释放后,ClassA
会被释放,ClassB
的 classA
属性会自动被设置为 nil
,从而打破循环引用。
2. 无主引用(Unowned References):
- 适用于两个对象的生命周期紧密相关,且一个对象的销毁不会影响另一个对象对其引用的情况。例如:
@interface ClassA : NSObject
@property (unowned, nonatomic) ClassB *classB;
@end
@interface ClassB : NSObject
@property (strong, nonatomic) ClassA *classA;
@end
这里 ClassA
对 ClassB
是无主引用,与弱引用不同,无主引用不会在对象释放后自动置为 nil
,使用时需要确保被引用对象还存在,否则会导致野指针错误。
ARC 在异常情况下的处理
在 ARC 环境下,当对象在释放过程中抛出异常时:
- 内存管理:ARC 会自动处理对象的释放和引用计数的调整,即使在异常情况下也能保证内存的一致性。例如,假设
ClassA
在 dealloc 方法中抛出异常,ARC 仍然会正确处理ClassA
所拥有的其他对象的释放(前提是这些对象没有参与循环引用等导致无法正常释放的情况),确保没有内存泄漏。 - 对象状态:ARC 会保证对象在异常发生时,其内部状态的一致性,尽量确保对象的析构过程尽可能完整。但是,如果异常导致对象析构不完全,可能会留下部分未清理的资源。不过,ARC 会尽力确保对象所占用的内存被正确回收,以避免内存泄漏问题。例如,如果
ClassA
持有一个文件描述符资源,在 dealloc 方法中释放文件描述符前抛出异常,虽然文件描述符可能没有被正确关闭,但ClassA
占用的内存会被回收,不会导致内存泄漏,只是文件描述符可能成为无效资源。在实际编程中,应尽量避免在对象释放过程中抛出异常,对于可能抛出异常的资源释放操作,应进行适当的异常处理。