Swift 5应用串联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

