【问题标题】:Why does this Swift struct require "mutating"? [duplicate]为什么这个 Swift 结构需要“变异”? [复制]
【发布时间】:2019-03-31 14:30:18
【问题描述】:

我遗漏了 Swift 中的可变性概念。我通常使用对象而不是结构来获得可观察性,因此值语义对我来说仍然是新事物。

struct Game {
  var map: [[Int]]

所以我在这里声明map 是可变的。那么为什么在这样的方法中......

  mutating func createPlayer() {
    // emptyLocation -> (Int, Int)
    let (X,Y) = emptyLocation()
    map[X][Y] = .player
  }

...我必须使用变异吗?是的,该函数正在变异,但原始结构是这样声明的。实际上,实际上每个func 似乎都是mutating,这似乎违背了标记的目的。

还有其他方法我应该这样做吗? mutating 的常见用法是否表明我应该避免的性能/内存问题?

更新:我对结构的内部状态“泄漏”到周围代码的方式感到非常不安;如果您在结构内声明成员var,那么它也必须在外部,即使您从未更改过。这违反了我能想到的所有封装概念。所以我将struct 更改为class,删除了所有mutating,并完成了它。我明白了,但我不确定我是否完全理解实施。对于这个 Swift-noob 来说,mutating 似乎是编译器可以在我不告诉它的情况下确定的东西 - 声明的成员是 var?,函数实际上会改变它吗?等等

【问题讨论】:

  • 如果你改变了值类型中的任何属性,值类型就会改变,从而改变关键字。
  • @J.Doe - 我明白了,但是由于原始的“对象”被声明为 var,那么声明变异的目的是什么?系统已经知道我声明它是可变的。

标签: swift immutability


【解决方案1】:

是的,structenum 的默认行为是实例方法不能修改属性,因为它们是值类型。所以你需要使用mutating 来覆盖这个行为。

定义属性 var 或 let 的方式仍然与是否可以从可变实例方法更改或直接更改相关。

由于您的财产不是私有的,您仍然可以这样做

var g = Game(map: [[1]])
g.map.append([2])

【讨论】:

  • 我发现可变性有点“泄露”到周围的代码中。在这种情况下,你不能做一个 let g =Game(),它必须是 var g=Game(),这似乎违反了封装。
  • @MauryMarkowitz 是的,但你可能会更好地使用一个类,结构肯定是很好的工具,但无论好坏,它们都比类更简单。
  • 在此处添加var 不会违反对值类型的封装。值类型的要点是调用者完全知道它的变异,并且变异不是在作用域之间隐式共享的。在值类型上说let 意味着“这不会改变”。因此,您可以标记它确实发生变化的地方(这样就不能在 let 值上调用这些方法)。
【解决方案2】:

在 Swift 中,结构体具有值语义,即它们的行为就好像它们是值一样,尽管其中一些像 StringArray 出于性能原因被实现为引用。当你改变这样一个结构时,如果你不是这个结构的唯一所有者,编译器实际上可能不得不制作一个副本;这被称为写时复制,它是变异表明的可能的性能问题。

【讨论】:

  • 那么我应该使用其他一些实现来避免这些 COW,或者这就是它的方式?
  • 通常这不是问题,但是当它是一个问题并且您确实想要引用语义而不是(即共享所有权)时,请使用引用类型(例如 NSArray)而不是值语义类型(数组) .
猜你喜欢
  • 2015-08-08
  • 1970-01-01
  • 2013-11-19
  • 1970-01-01
  • 2023-01-26
  • 2013-03-20
  • 2023-03-13
  • 2021-07-25
  • 1970-01-01
相关资源
最近更新 更多