Swift:使用弱自我维护基于块的执行中的原子性
Swift: Maintaining atomicity in a block-based execution using weak self
我经常看到像下面这样使用 weak self 的代码:
api.call() { [weak self] (result, error) in
if (error == nil) {
setGlobalState()
self?.doSomething()
} else {
setSomeErrorState()
self?.doSomethingElse()
}
}
但在我看来,如果 self 为 nil,则状态不一致,因为 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()
}
}
我对第一种情况缺乏原子性的担忧是否合理?对于使用 weak self 的块,后一种情况应该是最佳实践吗?
这个问题没有简单的答案,因为正确答案取决于 doSomething
和 doSomethingElse
在做什么,这里不清楚。
话虽如此,你说:
I often see code that uses weak self like below:
api.call() { [weak self] (result, error) in
if error == nil {
setGlobalState()
self?.doSomething()
} else {
setSomeErrorState()
self?.doSomethingElse()
}
}
But it seems to me that if self is nil, now the states are inconsistent because setGlobalState()
would have executed but self?.doSomething()
did not.
从技术上讲,这确实是个问题,但这是一种非常不寻常的情况。但如果这是一个问题,那很可能是更深层次问题的代码味道。
通常,当您看到类似这样的内容时,doSomething
或 doSomethingElse
正在更新 UI 或 self
所独有的内容,其中上述模式实际上是更可取。上面的代码片段确保 setGlobalState
或 setSomeErrorState
发生,但是如果 self
是,例如,视图控制器,它确保我们不会人为地保留它只是为了更新一个UI 不再存在。
但是如果 doSomething
和 doSomethingElse
不只是更新 UI,而是像您在后续评论中建议的那样“设置一些单例状态” ,那么你是对的,上面的内容不会达到你想要的。
那么,你接着说:
It would seem like the sensible thing to do is as follows:
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 服务不同步。
只有在以下情况下才使用第二种模式:例如,如果 setGlobalState
被调用,那么 doSomething
必须 是必不可少的称为;但是 (b) 如果 self
被释放,那么 两个 方法都不会被调用。
您可能要考虑第三种选择,即完全省略 [weak self]
:
api.call() { result, error in
if error == nil {
setGlobalState()
self.doSomething()
} else {
setSomeErrorState()
self.doSomethingElse()
}
}
如果 setGlobalState
和 doSomething
(或 setSomeErrorState
和 doSomethingElse
)必须在 API 调用完成后调用,那么我们根本就不会使用 [weak self]
。 (请注意,这仅在 api
被正确实现时才有效,设计为在完成时不会挂在闭包上,但所有设计良好的异步 API 无论如何都会这样做。)
不过,应该注意的是,如果 setGlobalState
和 doSomething
之间(或 setSomeErrorState
和 doSomethingElse
之间确实存在一些隐藏的全局依赖关系,那么暗示了设计中更深层次的问题。单独的对象应该是松耦合的。使用全局变量或有状态单例都是值得怀疑的,更不用说让应用程序开发人员承担保持两者同步的责任了。
最重要的是,这三个 api
完成选项的选择完全取决于 self
是什么,doSomething
做什么,以及 doSomethingElse
做什么。我不会断然拒绝第一种选择,因为它通常是正确的解决方案。
我经常看到像下面这样使用 weak self 的代码:
api.call() { [weak self] (result, error) in
if (error == nil) {
setGlobalState()
self?.doSomething()
} else {
setSomeErrorState()
self?.doSomethingElse()
}
}
但在我看来,如果 self 为 nil,则状态不一致,因为 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()
}
}
我对第一种情况缺乏原子性的担忧是否合理?对于使用 weak self 的块,后一种情况应该是最佳实践吗?
这个问题没有简单的答案,因为正确答案取决于 doSomething
和 doSomethingElse
在做什么,这里不清楚。
话虽如此,你说:
I often see code that uses weak self like below:
api.call() { [weak self] (result, error) in if error == nil { setGlobalState() self?.doSomething() } else { setSomeErrorState() self?.doSomethingElse() } }
But it seems to me that if self is nil, now the states are inconsistent because
setGlobalState()
would have executed butself?.doSomething()
did not.
从技术上讲,这确实是个问题,但这是一种非常不寻常的情况。但如果这是一个问题,那很可能是更深层次问题的代码味道。
通常,当您看到类似这样的内容时,doSomething
或 doSomethingElse
正在更新 UI 或 self
所独有的内容,其中上述模式实际上是更可取。上面的代码片段确保 setGlobalState
或 setSomeErrorState
发生,但是如果 self
是,例如,视图控制器,它确保我们不会人为地保留它只是为了更新一个UI 不再存在。
但是如果 doSomething
和 doSomethingElse
不只是更新 UI,而是像您在后续评论中建议的那样“设置一些单例状态” ,那么你是对的,上面的内容不会达到你想要的。
那么,你接着说:
It would seem like the sensible thing to do is as follows:
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 服务不同步。
只有在以下情况下才使用第二种模式:例如,如果 setGlobalState
被调用,那么 doSomething
必须 是必不可少的称为;但是 (b) 如果 self
被释放,那么 两个 方法都不会被调用。
您可能要考虑第三种选择,即完全省略 [weak self]
:
api.call() { result, error in
if error == nil {
setGlobalState()
self.doSomething()
} else {
setSomeErrorState()
self.doSomethingElse()
}
}
如果 setGlobalState
和 doSomething
(或 setSomeErrorState
和 doSomethingElse
)必须在 API 调用完成后调用,那么我们根本就不会使用 [weak self]
。 (请注意,这仅在 api
被正确实现时才有效,设计为在完成时不会挂在闭包上,但所有设计良好的异步 API 无论如何都会这样做。)
不过,应该注意的是,如果 setGlobalState
和 doSomething
之间(或 setSomeErrorState
和 doSomethingElse
之间确实存在一些隐藏的全局依赖关系,那么暗示了设计中更深层次的问题。单独的对象应该是松耦合的。使用全局变量或有状态单例都是值得怀疑的,更不用说让应用程序开发人员承担保持两者同步的责任了。
最重要的是,这三个 api
完成选项的选择完全取决于 self
是什么,doSomething
做什么,以及 doSomethingElse
做什么。我不会断然拒绝第一种选择,因为它通常是正确的解决方案。