iOS内存管理疑问:闭包场景下为何未触发循环引用?
iOS内存管理疑问解答:为何预期的循环引用未导致内存泄漏?
我了解ARC会通过对象的强引用计数来管理内存,当强引用计数变为0时对象会被释放,但以下代码让我产生了困惑:
final class SecondVC: UIViewController { let titleLabel: UILabel = UILabel() let network = Network() override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = .red self.view.addSubview(titleLabel) network.makeCall { self.titleLabel.text = "Title" } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.navigationController?.popViewController(animated: true) } deinit { print("SecondVC deinit called") } } final class Network { func makeCall(completion: @escaping () -> Void) { DispatchQueue.main.asyncAfter(deadline: .now() + 5) { } } deinit { print("Network deinit called") } }
我的理解是:SecondVC对Network持有强引用,同时Network通过闭包捕获了SecondVC的强引用,且completion回调从未被调用,按道理强引用计数不会变为0,但两者的deinit方法都被执行了。
我的问题是:是否存在内存泄漏?为何理论上不应被释放的对象却执行了deinit?
解答
不存在内存泄漏,核心原因如下:
- Network的
makeCall方法虽然接收了@escaping类型的completion闭包,但方法内部并没有将这个completion保存到类属性、全局变量或其他长期持有它的容器中。 - 当
makeCall方法执行完毕后,completion闭包就失去了所有强引用,它之前捕获的self(即SecondVC实例)的引用也随之被释放。 - 因此SecondVC和Network之间并未形成持续的循环引用,两者的强引用计数最终都能降为0,所以各自的
deinit方法会被正常调用。
内容的提问来源于stack exchange,提问作者artyom
相关产品推荐
相关产品推荐

