MST

星途 面试题库

面试题:在Objective-C的视图控制器中如何避免循环引用

假设你正在开发一个iOS应用,视图控制器之间存在复杂的导航关系,并且可能存在视图控制器之间的相互持有。请详细阐述在这种情况下,如何有效地避免循环引用,确保内存正常管理,并说明使用的内存管理策略和相关技术点。
23.6万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

避免循环引用及内存管理策略

  1. 使用弱引用(Weak References)
    • 在视图控制器之间的相互持有场景中,将其中一方的引用设置为弱引用。例如,若 ViewControllerA 持有 ViewControllerB,同时 ViewControllerB 也持有 ViewControllerA,可以将 ViewControllerB 中对 ViewControllerA 的引用声明为弱引用。在 Swift 中,可以使用 weak 关键字,如:
weak var viewControllerA: ViewControllerA?
- 在 Objective - C 中,使用 `__weak` 修饰符:
__weak ViewControllerA *viewControllerA;
- 弱引用不会增加对象的引用计数,当被引用的对象(如 `ViewControllerA`)释放时,弱引用会自动被设置为 `nil`,从而打破循环引用。

2. 使用无主引用(Unowned References) - 适用于相互持有且两个对象生命周期相同的场景。在 Swift 中,使用 unowned 关键字。例如:

unowned var viewControllerA: ViewControllerA
- 在 Objective - C 中没有直接对应的关键字,但原理类似,通过不增加引用计数来避免循环引用。不过使用无主引用时要注意,当被引用对象释放后,访问无主引用会导致运行时错误,所以要确保被引用对象先于持有它的对象释放。

3. 合理管理视图控制器的生命周期 - 遵循 iOS 视图控制器的生命周期方法。例如,在 viewDidLoad 中进行初始化操作,在 viewWillDisappeardeinit 中清理不需要的引用。在 Swift 中:

deinit {
    // 清理对其他视图控制器的强引用
    otherViewController = nil
}
- 在 Objective - C 中:
- (void)dealloc {
    // 清理对其他视图控制器的强引用
    self.otherViewController = nil;
}
  1. 使用协议(Protocols)和代理(Delegation)模式
    • 通过协议定义视图控制器之间的通信方法,将交互逻辑封装在协议方法中。持有关系通过代理来实现,代理通常设置为弱引用。例如,ViewControllerB 定义一个协议 ViewControllerBDelegateViewControllerA 遵循该协议并作为 ViewControllerB 的代理。
    • 在 Swift 中:
protocol ViewControllerBDelegate: AnyObject {
    func someMethod()
}

class ViewControllerB: UIViewController {
    weak var delegate: ViewControllerBDelegate?
}

class ViewControllerA: UIViewController, ViewControllerBDelegate {
    func someMethod() {
        // 实现协议方法
    }
}
- 在 Objective - C 中:
@protocol ViewControllerBDelegate <NSObject>
- (void)someMethod;
@end

@interface ViewControllerB : UIViewController
@property (weak, nonatomic) id<ViewControllerBDelegate> delegate;
@end

@interface ViewControllerA : UIViewController <ViewControllerBDelegate>
@end

@implementation ViewControllerA
- (void)someMethod {
    // 实现协议方法
}
@end
  1. 使用块(Blocks)时注意循环引用
    • 当在视图控制器中使用块来处理逻辑且块中捕获了视图控制器本身时,要使用弱引用或无主引用来避免循环引用。在 Swift 中,可以使用 [weak self][unowned self] 来捕获 self
let block = { [weak self] in
    self?.someMethod()
}
- 在 Objective - C 中,使用 `__weak typeof(self) weakSelf = self;` 来创建弱引用:
__weak typeof(self) weakSelf = self;
self.block = ^{
    [weakSelf someMethod];
};

相关技术点总结

  1. ARC(自动引用计数):iOS 自 iOS 5.0 引入 ARC,它自动管理对象的内存,通过引用计数来决定对象何时释放。理解 ARC 的工作原理对于避免循环引用至关重要,如强引用增加引用计数,弱引用和无主引用不增加引用计数等。
  2. 对象生命周期:深入理解视图控制器的生命周期,在合适的时机清理引用,确保内存及时释放,避免内存泄漏。
  3. 设计模式:代理模式和协议的合理使用可以解耦视图控制器之间的直接持有关系,降低循环引用的风险,同时提高代码的可维护性和可扩展性。
  4. 内存分析工具:使用 Instruments 中的 Leaks 工具来检测内存泄漏和循环引用。通过分析内存快照,可以找出潜在的循环引用对象,及时调整代码进行修复。