如何在struct属性上进行原子操作?

时间:2016-09-19 19:04:46

标签: swift struct

假设我有以下对象:

class Dog {
  let name: String
  var stats: Stats?
  func doSomething() { /*...*/ }
}

struct Stats {
  var weight: Double
  var age: Int
  func ageEquivalence() -> Int { /*...*/ }
}

...我想在Dog实例上执行一些计算。但是,我想原子地这样做。换句话说,我想保证执行所有代码,或者执行 no 代码。

为了澄清问题,首先,我们假设Statsclass。在这种情况下,这个问题很容易解决:

func updateAge(_ dog: Dog) {
  guard let stats = dog.stats else {
    return
  }

  stats.age += 1                            // A
  dog.doSomething()                         // B
  sendNotification(stats.ageEquivalence())  // C
}

如果存在统计数据,它将会执行A-C行,或者什么也不做。

现在让我们回到原来的问题,假设Stats再次是struct。如果我尝试运行上面的代码,首先我必须将let stats更改为var stats,否则编译器会抱怨我尝试更新常量。

func updateAge(_ dog: Dog) {
  guard var stats = dog.stats else {
    return
  }

  stats.age += 1                            // A
  dog.doSomething()                         // B
  sendNotification(stats.ageEquivalence())  // C
}

但是现在我们遇到statsdog.stats副本的问题,所以当我更新A行的年龄时,我实际上并没有修改原来的狗#39;年龄。

我们的下一个方法可能如下所示:

func updateAge(_ dog: Dog) {
  dog.stats?.age += 1                         // A
  dog.doSomething()                           // B
  if let stats = dog.stats {
    sendNotification(stats.ageEquivalence())  // C
  }
}

这种方法的问题在于,如果doSomething具有设置stats = nil的副作用,我们就会运行A& A行。 B,但不是C。

解决此问题的最佳方法是什么?

似乎问题的关键在于这一行:

dog.stats?.age += 1

为了修改struct,我们必须对原始对象采取行动,这意味着我们必须使用optional chaining。这不能与guard var一起使用,因为这会创建原始struct的副本,并且原始struct可能会因执行任何点的副作用而发生更改

0 个答案:

没有答案