编程iOS 开发者

描述 UIKit 中视图控制器的生命周期,以及对生命周期的不当处理如何导致内存泄漏或 UI 中的错误。应该知道哪些细节?

用 Hintsage AI 助手通过面试

答案

UIViewController 的生命周期包括关键方法:

  • init(nibName:bundle:) / init(coder:):初始化
  • loadView:创建视图层次结构
  • viewDidLoad:视图已加载(适合进行初步设置的地方)
  • viewWillAppear / viewDidAppear:视图显示/已显示
  • viewWillDisappear / viewDidDisappear:视图隐藏/已隐藏
  • deinit:删除控制器

方法调用顺序:

  • 首先创建视图层次结构(loadView,然后是 viewDidLoad
  • 每次在屏幕上显示和隐藏时调用 viewWillAppear/viewWillDisappear,然后是 viewDidAppear/viewDidDisappear
  • 删除后 - deinit

细节:

  • viewDidLoad 中不应访问视图的尺寸 — 它们可能不准确
  • deinitviewWillDisappear 中释放资源(观察者、定时器)很重要
  • 添加子视图 — 在 viewDidLoad 中,但不要在 viewWillAppear 中 — 否则每次出现时可能会重复添加

示例:

class MyViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() setupViews() setupBindings() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 订阅通知 } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 退订通知 } deinit { // 清理 } }

陷阱问题

问题:

是否允许视图控制器在生命周期之外访问视图的属性(例如,在初始化器中)?

答案: 不可以,在调用(或重写)loadView 之前,所有与视图相关的属性可能未初始化。任何在 loadView/viewDidLoad 之外访问它们的操作都将导致崩溃。

示例:

// 错误:self.view 还未初始化! init() { super.init(nibName: nil, bundle: nil) self.view.backgroundColor = .red // 崩溃 }

由于不知晓主题细节而导致的实际错误示例


故事

项目因过早访问 self.view 而崩溃 — 视图未创建,导致崩溃。


故事

在大型项目中,常常忘记在 viewWillDisappear 和/或 deinit 中从 NotificationCenter 或代理退订,从而导致内存泄漏(NotificationCenter 引用视图控制器,导致它不会被释放并继续监听事件)。


故事

viewWillAppear 中,每次出现时都向视图添加新的子视图(例如,显示加载指示器),而没有检查其是否存在。结果是每次返回屏幕时都会出现更多重复的指示器,导致 UI 出现故障。