导航控制器中视图控制器循环引用无法释放的解决方案咨询
首先得抓住问题核心:你的自定义UIImageView和UITextField用强引用持有视图控制器,而控制器本身又强持有这些自定义视图,双向强引用形成了循环,导致ARC无法自动释放控制器。
最根本的解决办法:将引用改为弱引用
直接修改自定义类里的controller变量声明,加上weak关键字即可打破循环:
// 自定义UIImageView类 class CustomImageView: UIImageView { weak var controller: UIViewController? // 改为弱引用 // ...其他业务代码 } // 自定义UITextField类 class CustomTextField: UITextField { weak var controller: UIViewController? // 改为弱引用 // ...其他业务代码 }
这样做的好处是:自定义视图对控制器的弱引用不会阻止控制器被释放,同时只要控制器存在,弱引用就能正常指向它,完全不需要手动置空,也不会影响你触发Segue的功能——当控制器被释放时,controller会自动变为nil,彻底解决内存泄漏问题。
若暂时无法修改自定义类(比如依赖第三方库)
可以在合适的时机手动将controller置为nil,推荐两个精准时机:
在视图控制器的
viewWillDisappear中判断是否被Pop/Dismiss:override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 仅当控制器即将被从导航栈移除时置空 if isMovingFromParent || isBeingDismissed { customImageView.controller = nil usernameTextField.controller = nil passwordTextField.controller = nil } }这个逻辑只会在控制器被Pop或Dismiss时生效,Push到下一个页面时不会影响,返回后
viewWillAppear中重新赋值就能恢复Segue功能。在Unwind Segue的处理逻辑中置空:
给Unwind Segue设置明确的Identifier,然后在来源控制器(比如B、C)的prepare(for:sender:)中判断:override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "unwindToA" { customImageView.controller = nil usernameTextField.controller = nil } }这种方式更精准针对Unwind场景,但需要给Segue配置唯一标识。
为什么你之前的viewWillAppear赋值没解决问题?
如果循环引用没被打破,控制器根本不会被释放,返回后还是原来的实例——你之前在Segue时置空controller,返回后viewWillAppear赋值其实是有效的,但核心的内存泄漏问题没解决。而改用弱引用后,既解决了泄漏,又不需要手动管理赋值和置空的逻辑。
总结:优先选择将自定义类的controller改为弱引用,这是最简洁、一劳永逸的方案。
内容的提问来源于stack exchange,提问作者user7804097

