如果使用索引,则Swift可以更改用let声明的结构,但如果使用循环则不能更改

时间:2018-12-15 21:12:41

标签: swift

在以下代码中,名为Card的结构被分配了let。然后,一旦分配,我将这张卡放入一个阵列。现在,在func resetCards中,我想将阵列中的每个卡都设置回其原始状态。但是,如果我对阵列中的每个卡使用一个for循环,则会收到一个错误消息"cannot assign property to constant",这是我所期望的。但是,如果执行以下操作:cards[0].variable = false,则不会出错,并且可以更改结构变量。为什么如果我使用for card in cards循环遍历数组,即使使用var声明了结构,也无法更改结构的属性,但是如果我使用数组索引访问结构,例如for index in cards.indices我可以吗?

class Concentration {
  var cards = [Card]()

  init(numberOfPairsOfCards: Int) {
    for _ in 0..<numberOfPairsOfCards {
      let card = Card()
      cards += [card, card]
    }

  func resetCards() {
    indexOfOneAndOnlyFaceUpCard = nil
    for card in cards {
      card.variable = true // this doesn't work
      cards[0].variable = true // this works
    }
  }
}

3 个答案:

答案 0 :(得分:4)

在放入数组之前如何“声明”该结构并不重要。让我们谈谈如何从数组访问事物。

我假设以下测试情况:

struct Card {
    var property : String
}
var cards = [Card(property:"hello")]

我们要说

for card in cards {
    card.property = "goodbye"
}

但是我们不能,因为card是用let隐式声明的,并且它的属性不能被突变。因此,让我们尝试通过var重新分配来解决此问题:

for card in cards {
    var card = card
    card.property = "goodbye"
}

现在我们的代码可以编译并运行,但是您猜怎么着?数组本身不受影响!这是因为card是数组中结构的副本;参数传递和赋值都将复制。实际上,我们可以通过坚持var的引用来简化这一点:

for var card in cards {
    card.property = "goodbye"
}

但是我们什么也得不到; card仍然是副本,因此数组内容仍然不受影响。

现在,让我们像在实验中一样尝试通过索引进行尝试:

for ix in cards.indices {
    cards[ix].property = "goodbye"
}

宾果!它编译并运行 并更改cards数组的内容。那是因为我们要直接访问数组中的每个卡 。就像我们说的一样:

for ix in cards.indices {
    var card = cards[ix]
    card.property = "goodbye"
    cards[ix] = card
}

是的,我们仍在制作副本,但是我们正在将该副本重新分配回阵列中的同一位置。索引访问是这样做的捷径。

但是,我们实际上仍在取出副本,对其进行变异并重新插入。我们可以尝试使用inout来解决此问题,但要花一些周详的计划,例如:

func mutate(card: inout Card) {
    card.property = "goodbye" // legal!
}
for ix in cards.indices {
    mutate(card: &cards[ix])
}

如您所见,我们现在可以设置card.property,因为对于inout,该参数隐式为var。但是,具有讽刺意味的是,我们仍在进行复制和替换,因为结构是一种值类型-即使通过分配进行赋值,它实际上也不会发生突变。 var引用给人一种我们这样做的错觉。

答案 1 :(得分:2)

结构是值类型,将它们分配给变量时将其复制。当您遍历值类型数组时:

for card in cards {

然后card包含每个元素的副本。任何更改都不会保存到原始阵列。

您可以遍历索引并直接访问数组值:

for offset in cards.indices {
  cards[offset].variable = true
}

但是,通常我们通常使用map来创建一个全新的数组:

cards = cards.map {
   var card = $0 // both `$0` and `card` are copies of the original
   card.variable = true 
   return card
}

答案 2 :(得分:1)

要回答为什么会出现编译错误的问题: 您需要将其声明为var而不是let,这是在for-in循环中省略单词时假定的

for var card in cards {
    card.variable = true
}

从长远来看,此答案无济于事,因为您仅更改卡结构的本地副本。您在“集中”状态下保留的纸牌阵列保持不变