You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift闭包Capture List疑问:weak/unowned self原理及捕获机制解惑

Hey,我太懂你这种困惑了——当年第一次啃Swift闭包的捕获列表时,我对着weak和unowned挠了好半天头,总觉得底层逻辑雾蒙蒙的。咱们一步步把这些点掰扯清楚:

Swift闭包捕获列表:从「捕获」到底层原理的清晰拆解

先搞懂「捕获」到底是什么?

首先,闭包是个引用类型,它最大的特点就是能访问定义它的上下文里的变量/常量——这个“把外部变量拉进自己作用域,并且保持关联”的动作,就是咱们说的「捕获」。

举个例子:你在ViewController里写了个闭包,里面用到了self.view,那这个闭包就捕获了self。默认情况下,闭包会对捕获的对象做强引用——也就是把对象的引用计数+1,这也是循环引用的根源(比如self持有闭包,闭包又持有self,俩都没法释放)。

weak self 和 unowned self 底层到底咋工作?

这俩都是用来改变闭包对self的引用类型,本质是控制引用计数:

  • weak self:闭包对self持有的是弱引用,底层就是个可选类型的指针。当self被系统释放时,这个指针会自动变成nil。所以用weak self的时候,你必须先解包(比如guard let self = self else { return }),因为它随时可能变成空的。这种方式最安全,适合不确定self生命周期是否比闭包长的场景。
  • unowned self:闭包对self持有的是无主引用,底层是个非可选的指针。它的核心假设是:self的生命周期一定比闭包长,所以不会自动变成nil。但如果self提前被释放了,再用unowned self就会直接触发崩溃(野指针访问)。这种适合你能100%确定闭包执行时self肯定存在的场景,比如同步执行的闭包(比如UIView.animate的完成闭包,只要当前VC没被释放,闭包执行时self一定在)。

闭包怎么在不持有对象的情况下用self?

其实就是通过捕获列表告诉闭包:“别对self做强引用,换个引用类型”。
比如把默认的强引用改成弱引用或者无主引用,这样闭包就不会增加self的引用计数,也就不会造成循环引用。

看个对比例子:

// 坑:强引用导致循环引用
class ViewController: UIViewController {
    // self持有这个闭包
    var completion: (() -> Void)?
    
    func setupBad() {
        completion = {
            // 闭包强引用self,循环引用诞生!
            self.view.backgroundColor = .red
        }
    }
}

// 正确:用weak self避免循环引用
func setupGood() {
    completion = { [weak self] in
        // 先解包,确保self还活着
        guard let self = self else { return }
        self.view.backgroundColor = .red
        // 这里的self是解包后的临时强引用,只在闭包执行期间有效,不会造成循环
    }
}

纠正你之前的误解:不是复制值类型那种栈传递

你之前以为是复制对象、像值类型一样在栈里传递,这个理解确实偏了。闭包是引用类型,它捕获的是对象的引用,不是复制对象本身。默认的强引用是让闭包持有这个引用的强引用(引用计数+1),而weak/unowned只是改变这个引用的类型,完全不是复制对象那回事。值类型的复制是完全独立的副本,而闭包捕获的引用是和原对象挂钩的,这本质上是两码事。


内容的提问来源于stack exchange,提问作者r.pul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:16:29