【问题标题】:Swift: Maintaining atomicity in a block-based execution using weak selfSwift:使用弱自我在基于块的执行中保持原子性
【发布时间】:2019-12-23 03:15:06
【问题描述】:

我经常看到下面这样使用弱自我的代码:

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()
  }
}

我对第一种情况缺乏原子性的担忧是否合法?对于使用弱自身的块,后一种情况是否应该是最佳实践?

【问题讨论】:

  • 我看到的代码基本上是设置一些单例状态。

标签: swift closures weak-references capture-list


【解决方案1】:

这个问题没有简单的答案,因为正确答案取决于 doSomethingdoSomethingElse 正在做什么,这里不清楚。

说了这么多,你说:

我经常看到下面这样使用弱自我的代码:

api.call() { [weak self] (result, error) in
  if error == nil {
    setGlobalState()
    self?.doSomething()
  } else {
    setSomeErrorState()
    self?.doSomethingElse()
  }
}

但在我看来,如果 self 为零,现在状态不一致,因为 setGlobalState() 会执行但 self?.doSomething() 没有。

从技术上讲,这确实可能是一个问题,但这是一种非常不寻常的情况。但如果这是一个问题,那很可能是更深层次问题的代码异味。

通常,当您看到这样的内容时,doSomethingdoSomethingElse 正在更新 UI 或 self 所独有的东西,其中上述模式实际上更可取。上面的代码 sn-p 确保 setGlobalStatesetSomeErrorState 发生,但如果 self 是,例如,视图控制器,它确保我们不会人为地保留它,只是为了更新一个没有更长的时间。

但是,如果doSomethingdoSomethingElse 不只是更新 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/doSomethingsetSomeErrorState/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 调用完成后同时调用 setGlobalStatedoSomething(或 setSomeErrorStatedoSomethingElse),那么我们根本不会使用 [weak self]。 (注意,这只有在 api 被正确实现的情况下才有效,被设计成在闭包完成时不会挂在闭包上,但无论如何,所有精心设计的异步 API 都会这样做。)

但是,应该注意的是,如果在 setGlobalStatedoSomething 之间(或在 setSomeErrorStatedoSomethingElse 之间)之间确实存在一些隐藏的全局依赖关系,那么这表明设计中存在更深层次的问题。单独的对象应该松散耦合。使用全局变量或有状态的单例是有问题的,更不用说让应用程序开发人员有责任保持两者同步了。

最重要的是,这三个api 补全选项的选择完全取决于self 是什么、doSomething 做什么以及doSomethingElse 做什么。我不会断然拒绝第一种选择,因为它通常是正确的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-21
    • 1970-01-01
    • 2014-05-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-10
    相关资源
    最近更新 更多