You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift 5应用串联24个ViewController后内存错误崩溃求助

问题:连续打开24个ViewController后因内存问题崩溃

我开发了一款学习类应用,通过以下代码串联多个ViewController:

let main = UIStoryboard(name: "Main", bundle: nil)
weak var levelCompletedViewController = main.instantiateViewController(withIdentifier: "levelCompletedViewController") as? LevelCompletedViewController
levelCompletedViewController?.modalPresentationStyle = .fullScreen
self.present(levelCompletedViewController!, animated: false)

应用总是在打开24个ViewController后崩溃(需求需要支持32个),报错信息:

Message from debugger: Terminated due to memory issue

原本计划在打开第32个ViewController时调用dismiss()触发所有VC的deinit方法,且每次在viewDidDisappear中都会清理所有图片及其他内存密集型变量。
我的内存指示器显示内存占用从未超过400MB,但应用每次都会在打开24个页面后崩溃。

我已在闭包中使用[weak self],禁用了僵尸对象,且不存在无限循环等问题(页面类型随机,但始终在24个时崩溃)。

请问是否有解决该问题的技巧,比如每次切换ViewController时重置根ViewController以触发deinit?

补充说明:
有建议在present新页面之前dismiss当前页面,但这样会导致我的自定义左右转场失效,转场代码如下:

let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromRight
self.view.window?.layer.add(transition, forKey: kCATransition)

解决思路与方案

1. 重置根ViewController(彻底解决内存堆积)

你提到的重置根VC方案是最直接有效的方式,能完全切断旧ViewController的引用链,确保系统触发deinit回收内存,同时还能保留自定义转场效果:

let main = UIStoryboard(name: "Main", bundle: nil)
guard let newVC = main.instantiateViewController(withIdentifier: "levelCompletedViewController") as? LevelCompletedViewController else { return }

// 应用自定义转场动画
let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromRight
self.view.window?.layer.add(transition, forKey: kCATransition)

// 替换根VC,旧VC链会被自动回收
if let window = UIApplication.shared.windows.first {
    window.rootViewController = newVC
}

这种方式不会产生VC堆积,无论打开多少个页面都能保持内存稳定。

2. 排查隐性内存泄漏

内存指示器显示的400MB可能未包含系统层内存开销(比如Core Animation缓存、未统计的私有对象),可以通过以下方式排查:

  • 使用Xcode的Memory Graph Debugger查看VC的引用链,确认是否存在未释放的强引用(比如代理未设为weak、通知未移除、闭包捕获遗漏[weak self])
  • 检查viewDidDisappear中的清理逻辑:确保图片置空(image = nil)、缓存清空、第三方资源(如WebView、播放器)彻底释放
  • 用autoreleasepool包裹VC实例化代码,加速临时对象回收:
autoreleasepool {
    let main = UIStoryboard(name: "Main", bundle: nil)
    guard let newVC = main.instantiateViewController(withIdentifier: "levelCompletedViewController") as? LevelCompletedViewController else { return }
    newVC.modalPresentationStyle = .fullScreen
    self.present(newVC, animated: false)
}

3. 兼容转场的dismiss方案(保留present链)

如果坚持使用present链,可以在转场完成后再dismiss当前VC,避免转场失效:

let main = UIStoryboard(name: "Main", bundle: nil)
guard let newVC = main.instantiateViewController(withIdentifier: "levelCompletedViewController") as? LevelCompletedViewController else { return }

// 添加自定义转场
let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromRight
self.view.window?.layer.add(transition, forKey: kCATransition)

self.present(newVC, animated: false) {
    // 转场完成后dismiss当前VC,避免内存堆积
    self.dismiss(animated: false)
}

不过这种方式需要注意VC的引用关系,不如重置根VC稳定。

4. 优化VC复用机制

避免每次从Storyboard实例化新VC,可自定义VC栈管理器,复用已创建的VC实例,减少内存中同时存在的VC数量:

  • 维护一个VC缓存池,根据页面ID复用实例
  • 或者改用UINavigationController的push/pop模式,配合popToViewController或popToRootViewController主动回收VC

内容的提问来源于stack exchange,提问作者Aurora1510

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 18:15:12