iOS Swift 的捕获列表 [weak self] 和 [unowned self]

捕获列表(capture list)是 Swift 中闭包的重要概念之一,用来控制闭包如何捕获和存储其上下文中的外部变量。

捕获行为

在闭包中使用外部变量时,Swift 会自动捕获这些变量的引用。如果这些变量是引用类型(如类实例),闭包会持有它们的强引用,可能导致强引用循环,即内存泄漏。

捕获列表允许你明确指定闭包应该如何捕获这些外部变量,特别是在需要避免强引用循环时,你可以使用捕获列表来指定变量的捕获方式,如使用 weak 或 unowned

捕获列表的语法

捕获列表在闭包表达式的起始位置,放在方括号 [] 中。每个捕获项都可以包含变量名、以及如何捕获该变量(weakunowned 等)。

语法格式:

{ [捕获列表] (参数) -> 返回类型 in
    // 闭包体
}

在 Swift 中,weak 和 unowned 都用于防止强引用循环,但它们有不同的特性和使用场景。核心区别在于,当引用的对象被释放时,它们的行为不同:

1. weak 引用

  • 自动设为 nil:当被引用的对象被释放时,weak 引用会自动变为 nil
  • 可选类型:由于 weak 引用可能会在对象被释放后变成 nil,因此它必须是可选类型Optional)。
  • 使用场景:通常用于避免强引用循环,特别是在对象的生命周期可能比引用的闭包短时(即闭包执行时对象可能已被释放)。
class MyClass {
    var value = 10
    func printValue() {
        let closure = { [weak self] in
            print(self?.value ?? 0)
        }
        closure() // 输出 10
    }
}

let myClass = MyClass()
myClass.printValue()
//在这个例子中,捕获列表 [weak self] 指定 self 以弱引用的方式捕获,避免了闭包对 self 的强引用,防止强引用循环。如果 self 在闭包执行时被释放,self 会变为 nil,因此使用 self? 来安全地访问其属性。

2. unowned 引用

  • 不会设为 nilunowned 引用不会变为 nil,即使被引用的对象已经被释放。
  • 非可选类型unowned 不需要声明为可选类型,因此它在使用时总是会假设对象存在。
  • 使用场景:适用于闭包执行期间,对象一定会存在的情况。对象被释放后,再访问 unowned 引用会导致程序崩溃(runtime error)。
class MyClass {
    var value = 10
    func printValue() {
        let closure = { [unowned self] in
            print(self.value)
        }
        closure() // 输出 10
    }
}

let myClass = MyClass()
myClass.printValue()
//unowned 表示闭包不持有对象的强引用,但假设对象在闭包执行期间仍然存在。如果在 unowned 的场景中访问已经释放的对象,会发生运行时崩溃。

总结区别:

特性weakunowned
是否会变为 nil 是的,自动变为 nil 否,不能变为 nil
是否必须是可选类型 是,必须为可选类型(Optional 否,不需要可选类型
是否会导致崩溃 不会,因为对象释放后会变成 nil 会,如果对象已经释放,访问 unowned 会崩溃
使用场景 对象生命周期可能比闭包短 对象生命周期在闭包执行期间一定存在

何时使用:

  • 使用 weak:当你不确定对象的生命周期,可能会在闭包执行时已经被释放的情况下,使用 weak。(弱引用)
  • 使用 unowned:当你确定对象在闭包的整个执行过程中都不会被释放时,可以使用 unowned。(无主引用)

 

posted on 2024-10-22 10:56  ACM_Someone like you  阅读(28)  评论(0编辑  收藏  举报

导航