Xcode Visual Memory Debugger排查lazy视图关闭VC后未释放问题
问题原因与修复方案
lazy var 本身不会导致对象无法释放,延迟初始化语法和内存泄漏没有关联,当前代码存在明确的强引用链异常,属于代码编写错误,具体问题点如下:
- 跨父视图层级的强引用残留
你在HeadeViewMenu中将外部传入的tabBarController.view强引用为自身属性,同时将内部的tabBarView直接添加到了这个属于全局根层级的视图上:
调用时传入的// 错误点1:强引用不属于自身层级的父视图 var tabBar: UIView! func setupTabBar(view: UIView) { guard tabBarHeight != nil else { return } tabBar = view // 这里对传入的tabBarController.view是强持有 tabBar.addSubview(tabBarView) // UIKit中父视图会强持有所有子视图 // 省略约束代码 }(self.tabBarController?.view)!是被根控制器全局持有的视图,不会随业务VC销毁。你的tabBarView被这个全局视图强持有,就算业务VC执行pop/dismiss操作销毁,这条引用链也不会断开,最终导致HeadeViewMenu连同内部的HeaderMenuCell、UITableView等子对象都无法被释放。 - 销毁判断逻辑错误
- 你在
viewDidDisappear中手动置空delegate是无效操作:首先delegate本身已经声明为weak,本来就不会产生循环引用;其次viewDidDisappear仅代表视图离开屏幕,不代表VC被销毁(比如push切换页面时也会触发该方法)。 - 仅通过内存图谱看到对象存在不能直接判定为内存泄漏:内存图谱存在已释放对象的缓存残留可能,判断是否释放的金标准是看对应类的
deinit方法是否执行——你已经在HeadeViewMenu中写了deinit打印,持有该视图的VC也可以添加deinit打印,返回根控制器后如果两个deinit日志都正常打印,就说明对象已经被正常回收。
- 你在
- 其他易忽略的引用问题
你代码中所有闭包内使用self目前没有循环引用问题(UIView动画闭包不会强持有self),UITableView的delegate/dataSource在iOS9之后已经是weak声明,不会因为tableView持有self产生循环引用。
修复步骤
- 将
HeadeViewMenu中的tabBar属性改为弱引用:// 原代码 var tabBar: UIView! weak var tabBar: UIView? - 在持有
P_headerViewNewMenu的VC的deinit方法中,手动将跨层级添加的tabBarView从父视图移除:deinit { P_headerViewNewMenu.tabBarView.removeFromSuperview() print("VC deinit called") } - 删除
viewDidDisappear中置空delegate的无效代码,该操作没有实际作用。 - 验证:返回根控制器后,查看控制台是否打印VC和
HeadeViewMenu的deinit日志,正常打印即代表内存释放正常。

内容的提问来源于stack exchange,提问作者Ice
相关产品推荐
相关产品推荐

