Swift — 闭包(closure)

在捕获列表中使用弱引用 #

[weak self] 是 Swift 中闭包(closure)捕获列表的一部分,用于避免强引用循环(retain cycles)。在使用闭包时,特别是在异步操作或定时器中,闭包可能会捕获其上下文中的对象(如 self),这可能导致内存泄漏。以下是对 [weak self] 的详细解释:

updateTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
    self?.currentTime = self?.audioPlayer?.currentTime ?? 0
}

1. 强引用循环 #

当一个对象(例如,类的实例)持有对另一个对象的强引用,而另一个对象又持有对第一个对象的强引用时,就会形成强引用循环。这种情况下,两个对象都无法被释放,从而导致内存泄漏。

例如,在 AudioPlayer 类中,如果定时器的闭包捕获了 self(即 AudioPlayer 的实例),而 AudioPlayer 又持有对定时器的强引用,就会形成循环引用。

2. 使用 [weak self] #

通过在闭包的捕获列表中使用 [weak self],你可以创建一个对 self 的弱引用。这样,闭包不会增加 self 的引用计数,从而避免强引用循环。

updateTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
    self?.currentTime = self?.audioPlayer?.currentTime ?? 0
}

在这个例子中:

  • self? 是一个可选链(optional chaining),如果 self 已经被释放(即为 nil),闭包中的代码不会执行,从而避免了潜在的崩溃。
  • 如果 self 仍然存在,闭包将安全地访问 self 的属性和方法。

3. 何时使用 [weak self] #

  • 异步操作:在网络请求、定时器、动画等异步操作中,使用 [weak self] 是一种常见的做法。
  • 避免内存泄漏:当你知道闭包会持有对某个对象的引用,而这个对象又持有对闭包的引用时,使用 [weak self] 可以有效避免内存泄漏。

4. 其他捕获修饰符 #

除了 weak,Swift 还提供了 unowned 捕获修饰符:

  • unowned:与 weak 类似,但 unowned 不会创建可选类型的引用。如果引用的对象被释放,访问 unowned 引用会导致运行时崩溃。因此,unowned 适用于你确定引用在闭包生命周期内始终存在的情况。

总结 #

使用 [weak self] 是一种良好的编程实践,特别是在处理闭包时,可以有效避免内存泄漏和强引用循环。

本文共 731 字,上次修改于 Dec 31, 2024