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

Swift弱引用和强引用:别让对象偷偷‘赖着不走’

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

Swift 时,你有没有遇到过这种状况:界面关了,但内存占用没降;定时器还在跑,数据却已经没了;或者一个 ViewController 明明 pop 了,它的 deinit 就是不执行?十有八九,是引用关系在‘捣鬼’。

强引用:默认的‘死拽不放’

Swift 默认所有对象赋值都是强引用。比如:

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

let p1 = Person(name: "小王")
let p2 = p1 // p2 强引用 p1

只要还有至少一个强引用指着它,这个对象就一直活在内存里——哪怕你心里早把它‘拉黑’了。

弱引用:说好‘不拦着你走’

当两个对象互相强引用,又没有外部干预,就会卡死在内存里,形成循环引用(retain cycle)。比如常见的 delegate 场景:

class ViewController {
    var dataSource: DataSource? // 强引用
    
    func loadData() {
        dataSource?.fetch()
    }
}

class DataSource {
    weak var delegate: ViewController? // 关键:weak!
    
    func fetch() {
        delegate?.updateUI() // 安全调用
    }
}

这里把 delegate 声明为 weak var,意思是:“我只临时看看你,你不在我也不拦着,你走了我自动变成 nil。”这样 ViewController pop 后能正常释放,DataSource 也不会拖着它不放。

什么时候必须用 weak?

记住这三种典型场景:

  • 代理(delegate):比如 UITableViewDelegate、自定义回调对象;
  • 闭包中捕获 self:如果闭包被对象长期持有(如网络请求 completion),又反过来被 self 持有,就得用 [weak self]
  • 父-子视图关系里的反向引用:比如子 View 想通知父 View,父 View 是强引用它的,那子 View 的回调属性就得是 weak

举个闭包例子:

class NetworkManager {
    func request(completion: @escaping (Data) -> Void) {
        // 模拟异步请求
        DispatchQueue.main.async {
            let data = Data()
            completion(data)
        }
    }
}

class ViewController: UIViewController {
    let manager = NetworkManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        manager.request { [weak self] data in
            guard let self = self else { return }
            self.update(with: data) // 防止 self 已释放
        }
    }
    
    func update(with data: Data) { /* ... */ }
}

加了 [weak self],闭包就不会强留 ViewController 在内存里。配合 guard let self = self,还能安全解包,避免崩溃。

weak 不是万能的,unowned 要小心

有人会问:为啥不用 unowned?它不也绕过强引用吗?是,但它不安全——一旦对象提前释放,再访问就直接 crash。除非你 100% 确定生命周期,否则优先选 weak + 可选绑定,更稳。

装机要挑兼容配件,写代码也得配对引用类型。强引用管‘活着’,弱引用管‘放手’,搞清谁该拽着、谁该松手,内存才不会越积越厚,App 才跑得轻快利落。