为何预期存在Retain Cycle的代码未被Instruments检测到内存泄漏?
我在学习**Retain Cycle(循环引用)**时编写了一段理论上会产生内存泄漏的代码,但使用Instruments工具检测后并未发现泄漏。
相关代码如下:
import UIKit class ViewController: UIViewController { private var button = { let _button = UIButton() _button.setTitle("Tap Me", for: .normal) _button.setTitleColor(.systemBlue, for: .normal) return _button }() override func viewDidLoad() { super.viewDidLoad() view.addSubview(button) button.addTarget(self, action: #selector(actionButton), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.centerYAnchor.constraint(equalTo: view.centerYAnchor), button.widthAnchor.constraint(equalToConstant: 80), button.heightAnchor.constraint(equalToConstant: 30) ]) } @objc private func actionButton() { let secondVC = ViewController2() present(secondVC, animated: true) } } class MyView: UIView { let viewContoller: ViewController2 init(from vc: ViewController2) { viewContoller = vc super.init(frame: .zero) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } class ViewController2: UIViewController { var myView: MyView! override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .red myView = MyView(from: self) } }
代码逻辑:点击ViewController的按钮会弹出ViewController2,ViewController2会初始化持有自身引用的MyView,按道理关闭ViewController2时应该形成循环引用,但实际ViewController2的deinit方法能正常执行,Instruments也显示无泄漏,想知道问题出在哪里?
问题解析:循环引用未被外部强持有,ARC自动回收
循环引用导致内存泄漏的核心前提是:循环中的对象被外部强引用持有,当外部引用释放后,循环内的对象因互相强引用无法被回收。
你的代码里确实存在双向强引用:
- ViewController2通过
var myView: MyView!强持有MyView - MyView通过
let viewContoller: ViewController2强持有ViewController2
但关键在于,当你关闭ViewController2时,present它的ViewController会释放对ViewController2的强引用,此时整个循环(ViewController2 + MyView)没有被任何外部对象强持有。ARC会识别到这组对象不再被任何代码需要,会将它们全部销毁,因此不会产生内存泄漏。
如何制造真正的内存泄漏
如果要让这段代码产生预期的泄漏,只需让循环被外部强引用持有即可。比如修改ViewController,让它持有ViewController2的引用:
class ViewController: UIViewController { private var secondVC: ViewController2? // 新增外部强引用 @objc private func actionButton() { secondVC = ViewController2() present(secondVC!, animated: true) } }
此时关闭ViewController2后,ViewController仍然持有secondVC的强引用,而secondVC和MyView互相引用,即使dismiss了ViewController2,它也无法被销毁,Instruments就能检测到内存泄漏。
内容的提问来源于stack exchange,提问作者user121095

