我经常看到使用弱自我的代码,如下所示:
api.call() { [weak self] (result, error) in
if (error == nil) {
setGlobalState()
self?.doSomething()
} else {
setSomeErrorState()
self?.doSomethingElse()
}
}
但在我看来,如果 self 为零,现在状态不一致,因为 setGlobalState(( 会执行但 self?。doSomething(( 没有。
明智的做法似乎是:
api.call() { [weak self] (result, error) in
guard let self = self else { return }
if (error == nil) {
setGlobalState()
self.doSomething()
} else {
setSomeErrorState()
self.doSomethingElse()
}
}
我对第一种情况下缺乏原子性的担忧是否合理?当涉及到使用弱自我的块时,后一种情况应该是最佳实践吗?
这个问题没有简单的答案,因为正确答案取决于doSomething
和doSomethingElse
在做什么,这里不清楚。
话虽如此,你说:
我经常看到使用弱自我的代码,如下所示:
api.call() { [weak self] (result, error) in if error == nil { setGlobalState() self?.doSomething() } else { setSomeErrorState() self?.doSomethingElse() } }
但在我看来,如果自我为零,现在状态是不一致的,因为
setGlobalState()
会执行,但self?.doSomething()
没有。
从技术上讲,这确实可能是一个问题,但这是一个非常不寻常的情况。但如果这是一个问题,那可能是更深层次问题的代码气味。
通常,当您看到类似这样的内容时,doSomething
或doSomethingElse
正在更新 UI 或任何self
所特有的内容,其中上述模式实际上是可取的。上面的代码片段可确保发生setGlobalState
或setSomeErrorState
,但如果self
是视图控制器,则可确保我们不会人为地保留它,只是为了更新不再存在的 UI。
但是,如果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() } }
这里的挑战是,如果在调用 API 完成处理程序之前解除分配self
,那么它不会执行任何操作。您已执行 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
做什么。我不会断然拒绝第一种选择,因为它通常是正确的解决方案。