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

Swift中[unowned self]放置位置困惑及循环引用问题解疑

解决ViewController循环引用:Case1和Case2的unowned用法差异

嘿,我来帮你搞清楚这两个场景下循环引用的问题,以及[unowned self]该怎么放!首先得明确:循环引用的本质是两个对象互相强引用,形成无法打破的引用闭环,导致它们的deinit都不会被调用。下面我用具体代码示例拆解两种常见场景:

Case 1:闭包是ViewController的直接属性

这种场景里,ViewController直接持有一个闭包属性,而闭包内部又引用了self(ViewController),形成闭环。

有问题的代码:

class ViewController: UIViewController {
    // 闭包属性:默认是强引用
    var onCompletion: (() -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 闭包内部强引用self,形成循环
        onCompletion = {
            print("当前页面标题:\(self.title ?? "无标题")")
        }
    }
    
    deinit {
        print("ViewController 被释放了!")
    }
}

循环链条:ViewController → 强持有 onCompletion 闭包 → 闭包强持有 self(ViewController)→ 闭环形成,两者都无法释放。

正确的解决方式:

把[unowned self]加到闭包的捕获列表里,让闭包不再强引用ViewController:

onCompletion = { [unowned self] in
    print("当前页面标题:\(self.title ?? "无标题")")
}

这样闭环被打破,当ViewController被销毁时,闭包也会被释放,deinit就能正常调用了。

Case 2:闭包被其他对象持有,且ViewController持有该对象

这种场景里,ViewController强持有一个第三方对象(比如网络管理器、工具类),然后调用该对象的方法时传入闭包,闭包内部引用self,形成跨对象的循环引用。

有问题的代码:

class APIManager {
    // @escaping 表示闭包会被保存下来(非立即执行)
    func fetchData(completion: @escaping () -> Void) {
        // 模拟异步任务
        DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
            completion()
        }
    }
}

class ViewController: UIViewController {
    // VC强持有APIManager实例
    let apiManager = APIManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 传入的闭包强引用self,形成循环
        apiManager.fetchData {
            print("数据加载完成,当前页面标题:\(self.title ?? "无标题")")
        }
    }
    
    deinit {
        print("ViewController 被释放了!")
    }
}

循环链条:ViewController → 强持有 apiManager → apiManager 强持有 completion 闭包 → 闭包强持有 self(ViewController)→ 闭环形成。

正确的解决方式:

同样把[unowned self]加到传入的闭包捕获列表里,而不是加到APIManager内部的闭包:

apiManager.fetchData { [unowned self] in
    print("数据加载完成,当前页面标题:\(self.title ?? "无标题")")
}

这样闭包不再强引用ViewController,闭环被打破,deinit就能正常触发。

为什么会出现“一种有效一种无效”?

大概率是你把[unowned self]加错了位置:

  • 如果在Case1里,你没把[unowned self]加到ViewController自己的闭包属性里,而是加在了其他地方,肯定无效;
  • 如果在Case2里,你错误地把[unowned self]加到了APIManager内部的闭包(比如DispatchQueue.global().asyncAfter里的闭包),而不是加到传入的completion闭包,那也无法打破循环——因为真正强引用self的是你传入的那个闭包,不是APIManager内部的闭包。

额外提醒:unowned vs weak

  • 用[unowned self]的前提是:闭包执行期间,self绝对不会被释放(比如ViewController肯定在闭包执行时还存在);
  • 如果self可能在闭包执行前就被释放(比如页面被提前pop),建议用[weak self],并在闭包内部判断self是否为nil,避免野指针崩溃。

内容的提问来源于stack exchange,提问作者Kesong Xie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:37:16