在 Swift 中,如果我有一个捕获 [weak self] 的闭包,在闭包开始时解包可选的 self 是一种好习惯吗?

In Swift, if I have a closure capturing [weak self], is it good practice to unwrap the optional self at the beginning of the closure?

我正在为 macOS 应用程序使用 Swift,Xcode 是 12.5.1。假设我有以下代码:

func performAsyncTask(completion: { [weak self] (error: Error?) in 

     self?.someProperty = 0.0
     self?.someOtherProperty = 0.0
     // Other similar statements follow
})

改成:

是不是不错的选择
func performAsyncTask(completion: { [weak self] (error: Error?) in 
     
     guard let self = self else { return }
     self.someProperty = 0.0
     self.someOtherProperty = 0.0
     // Other similar statements follow
})

我相信第一个实现更好,因为 self 可能在 语句之间变成 nil,所以在开始时解包可能不太干净。我希望专家能给我指明正确的方向。感谢您的关注。

I believe that the first implementation is better because self could become nil between the statements

这就是 second 实施实际上更好的原因!如果 self 在第一个语句中不为 nil,则第一个语句使得 self 不能 在语句之间变为 nil。它为块的其余部分保留了 self。这就是所谓的“强弱之舞”。

guard let self = self else { return }
      //  ^^^^ this self is _strong_, not weak; it retains self

出于与您给出的相同原因,我会以另一种方式争论(在您的第一个版本中,self 可以在您的关闭代码 运行s 的任何时候被释放)。

如果闭包 运行 位于与保留 self 的代码不同的线程上,则对象可能会在引用 self? 的不同语句之间变为 nil。 (这不太可能,但有可能。)如果你的闭包代码做的事情比仅仅将属性分配给 self 更复杂,那么在它中间释放 self 可能会使整个闭包变得毫无意义,甚至使其失败. (假设您正在进行图像处理,而图像在处理过程中消失了。)

在第二个版本中,guard let self = self 语句在闭包范围内创建对 self 的强引用,这意味着 self 引用的对象在闭包完成之前不会被释放.

我宁愿知道 self 是否仍然存在于闭包的开头,然后 运行 完整的闭包代码,而不是必须防御性地编写我的闭包代码以继续检查“有自我被释放了?现在怎么样?现在?好的,现在怎么样?

就编码风格和可读性而言,不得不不断地处理可选的 chaining/binding/nil 合并是一件痛苦的事情,并且会使您的代码更难理解