电脑之家
柔彩主题三 · 更轻盈的阅读体验

Swift弱引用和强引用:一招搞懂循环引用怎么破

发布时间:2026-04-08 13:30:28 阅读:2 次

Swift 时,你有没有遇到过这样的情况:对象明明该被释放了,内存却一直涨?界面跳转几次后卡顿变明显?后台任务结束后 UI 还在偷偷调用已销毁的 ViewController?八成是循环引用在作怪——而它的根源,就在强引用弱引用没用对。

强引用:默认就“抓得死死的”

Swift 默认所有对象引用都是强引用。只要有一个强引用指向它,对象就不会被释放。比如:

class Person {
    let name: String
    init(name: String) { self.name = name }
}

let p1 = Person(name: "小明")
let p2 = p1  // p2 强引用 p1 指向的对象

这时候,p1 和 p2 都强引用着同一个 Person 实例。只有当 p1 和 p2 都被设为 nil,这块内存才会真正回收。

弱引用:只“瞄一眼”,不拦着释放

weak 声明的引用不会增加对象的引用计数,也不会阻止对象被释放。它天生就是为解决双方互相强引用而生的,典型场景就是 delegate 或闭包回调。

比如一个网络请求管理器,回调时要更新 ViewController 的 UI:

class NetworkManager {
    weak var delegate: ViewController?  // 关键:这里必须 weak!
    
    func fetchData() {
        // 模拟请求完成
        delegate?.updateUI(data: "新数据")
    }
}

如果这里写成 var delegate: ViewController?(强引用),那 ViewController 持有 NetworkManager,NetworkManager 又强持有 ViewController —— 谁也不放谁,内存就卡死了。

闭包里的陷阱:[weak self] 不是摆设

闭包默认会强捕获它用到的外部变量。下面这段代码,ViewController 就可能永远不释放:

networkManager.fetch { [self] data in
    self.label.text = data  // self 被强捕获
}

正确写法是:

networkManager.fetch { [weak self] data in
    guard let self = self else { return }
    self.label.text = data
}

注意两点:一是 [weak self],二是解包后再用。这样即使 ViewController 半路 pop 掉了,闭包也不会把它拽回来。

什么时候用 unowned?小心点

unownedweak 类似,也不增加引用计数,但它不变成 nil,而是直接崩溃(类似强制解包)。只适合你 100% 确定生命周期一定比闭包长的场景,比如 UIViewController 和它的子 view。日常开发中,优先选 weak 更安全。

快速自查小技巧

在 Xcode 中打开 Memory Graph Debugger(调试菜单 → Debug Workflow → View Memory Graph),运行 App 后点几下,如果看到两个对象之间画着双向实线箭头,基本就是循环引用了。顺着箭头找 strong,换成 weak 或加 [weak self] 就行。

别把弱引用当成“可有可无”的修饰词,它是 Swift 内存管理里最常踩坑也最容易修复的一环。改两行代码,App 流畅度可能就上一个台阶。