基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

闭包中的 [weak self] 和 [unowned self] 有什么区别?何时使用 unowned 是安全的?

知识点图片

在 Swift 的闭包(Closure)中使用 [weak self][unowned self] 都是为了解决 循环引用(Retain Cycle) 的问题,但它们在处理对象生命周期的方式上有本质的区别。

以下是详细的对比和使用建议:

1. 核心区别

特性 [weak self] (弱引用) [unowned self] (无主引用)
类型 可选类型 (Optional),即 Self? 非可选类型 (Non-Optional),即 Self
引用计数 不增加引用计数 不增加引用计数
对象释放后的行为 如果 self 被释放,引用自动变为 nil 如果 self 被释放,引用不会变为空,访问它会导致 崩溃 (Crash)
代码处理 需要解包 (如 guard let self = self else { return }) 可以直接使用 self,不需要解包
安全性 安全。即使对象没了,代码也不会崩 不安全。如果对象没了还去访问,程序会崩溃

2. 代码示例对比

使用 [weak self]

由于 self 变成了可选类型,你需要处理它可能为 nil 的情况。

plaintext
class NetworkManager {
    func fetchData(completion: @escaping () -> Void) {
        // 模拟网络请求
        DispatchQueue.global().async { [weak self] in
            // 1. 使用可选链
            self?.doSomething()
            
            // 2. 或者使用 guard let 解包 (推荐)
            guard let self = self else {
                print("对象已被释放,停止执行")
                return
            }
            self.doSomething()
        }
    }
    
    func doSomething() { print("Done") }
}

使用 [unowned self]

你向编译器保证:“当这个闭包执行的时候,self 一定还活着。”

plaintext
class BankAccount {
    var balance: Double = 0.0
    
    lazy var printBalance: () -> Void = { [unowned self] in
        // 不需要解包,直接使用,因为我们确信 self 存在
        print("当前余额: \(self.balance)")
    }
}

3. 何时使用 unowned 是安全的?

使用 unowned 的黄金法则是:闭包的生命周期 ≤ self 的生命周期

也就是说,闭包不可能在 self 被释放后还在运行。如果存在任何“闭包可能比对象活得久”的可能性,就绝对不能用 unowned

✅ 安全场景 (可以使用 unowned)

  1. Lazy 属性初始化
    当闭包作为一个属性定义在类中,且该闭包只在这个类的内部使用,不会被传递到外部去异步执行时。

    • 例子:上面的 BankAccount 例子。printBalance 闭包属于 BankAccount 实例,如果实例销毁了,闭包也就随之销毁了,不存在“实例没了闭包还在跑”的情况。
  2. 确定的父子关系
    当闭包被一个子对象持有,而子对象的生命周期严格受控于父对象(self),且子对象在父对象销毁时也会立即销毁。

    • 例子:一个 View Controller 持有一个自定义 View,View 中有一个闭包回调。如果 View Controller 释放了,View 也会释放,闭包也就没了。

❌ 不安全场景 (必须使用 weak)

  1. 异步操作 (Asynchronous Operations)
    这是最容易导致 Crash 的地方。网络请求、定时器、GCD 延迟调用等。

    • 原因:你发起一个网络请求,用户退出了当前界面(self 被释放)。几秒后网络请求回调执行,如果用 unowned,此时访问 self 就会崩溃。
  2. 闭包被存储在其他地方
    如果闭包被传递给了单例、或者其他生命周期比 self 长的对象持有时。


4. 总结与最佳实践

虽然 unowned 少了一次解包的过程,性能开销微乎其微地小于 weak,但在现代 iOS 开发中,为了代码的健壮性,建议遵循以下原则:

  1. 默认使用 [weak self]:除非你有 100% 的把握闭包和 self 同生共死,否则用 weak。多写一行 guard let 换来的是 App 不会因为意外的生命周期问题而闪退。
  2. 在异步回调中永远使用 [weak self]:网络请求、动画完成回调等。
  3. 仅在 Lazy 属性或紧密耦合的同步逻辑中使用 [unowned self]

一句话口诀:

怕死用 weak,命硬(确定同生共死)用 unowned。如果不确定,就用 weak

00:00
00:00