在以下代码中,名为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
}
}
}
答案 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
}
从长远来看,此答案无济于事,因为您仅更改卡结构的本地副本。您在“集中”状态下保留的纸牌阵列保持不变