这个问题没有简单的答案,因为正确答案取决于 doSomething 和 doSomethingElse 正在做什么,这里不清楚。
说了这么多,你说:
我经常看到下面这样使用弱自我的代码:
api.call() { [weak self] (result, error) in
if error == nil {
setGlobalState()
self?.doSomething()
} else {
setSomeErrorState()
self?.doSomethingElse()
}
}
但在我看来,如果 self 为零,现在状态不一致,因为 setGlobalState() 会执行但 self?.doSomething() 没有。
从技术上讲,这确实可能是一个问题,但这是一种非常不寻常的情况。但如果这是一个问题,那很可能是更深层次问题的代码异味。
通常,当您看到这样的内容时,doSomething 或 doSomethingElse 正在更新 UI 或 self 所独有的东西,其中上述模式实际上更可取。上面的代码 sn-p 确保 setGlobalState 或 setSomeErrorState 发生,但如果 self 是,例如,视图控制器,它确保我们不会人为地保留它,只是为了更新一个没有更长的时间。
但是,如果doSomething 和doSomethingElse 不只是更新 UI,而是按照您在后续评论中的建议“设置一些单例状态”,那么您是对的,以上是达不到你想要的。
所以,你继续说:
看起来明智的做法如下:
api.call() { [weak self] (result, error) in
guard let self = self else { return }
if error == nil {
setGlobalState()
self.doSomething()
} else {
setSomeErrorState()
self.doSomethingElse()
}
}
这里的挑战是,如果self 在调用 API 完成处理程序之前被释放,那么它就不会做任何。您已执行 API 调用,但不会调用 setGlobalState/doSomething 和 setSomeErrorState/doSomethingElse。因此,如果您真的要设置全局状态和单例属性,那么它们将在内部保持一致是对的,但它们现在可能与您的 Web 服务不同步。
仅当 (a) 必须满足以下条件时才使用第二种模式,例如,如果调用了 setGlobalState,则还必须调用 doSomething ;但是 (b) 如果 self 被解除分配,那么 这些方法都不会被调用。
您可能需要考虑第三种选择,即完全省略 [weak self]:
api.call() { result, error in
if error == nil {
setGlobalState()
self.doSomething()
} else {
setSomeErrorState()
self.doSomethingElse()
}
}
如果必须在 API 调用完成后同时调用 setGlobalState 和 doSomething(或 setSomeErrorState 和 doSomethingElse),那么我们根本不会使用 [weak self]。 (注意,这只有在 api 被正确实现的情况下才有效,被设计成在闭包完成时不会挂在闭包上,但无论如何,所有精心设计的异步 API 都会这样做。)
但是,应该注意的是,如果在 setGlobalState 和 doSomething 之间(或在 setSomeErrorState 和 doSomethingElse 之间)之间确实存在一些隐藏的全局依赖关系,那么这表明设计中存在更深层次的问题。单独的对象应该松散耦合。使用全局变量或有状态的单例是有问题的,更不用说让应用程序开发人员有责任保持两者同步了。
最重要的是,这三个api 补全选项的选择完全取决于self 是什么、doSomething 做什么以及doSomethingElse 做什么。我不会断然拒绝第一种选择,因为它通常是正确的解决方案。