Swift может изменить структуру, объявленную с помощью let, если используется индекс, но не при использовании цикла - PullRequest
0 голосов
/ 16 декабря 2018

В приведенном ниже коде структура с именем 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 голосов
/ 16 декабря 2018

Чтобы ответить на вопрос, почему вы получаете ошибку компиляции: вам нужно объявить ее как var вместо let, что предполагается при пропуске слова в цикле for-in

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

Этот ответ не поможет вам в долгосрочной перспективе, так как вы изменяете только локальную копию структуры карты.Массив карт, которые вы сохраняете в Концентрации, остается неизменным

0 голосов
/ 16 декабря 2018

То, как структура «объявлена» перед тем, как поместить ее в массив, на самом деле не имеет значения.Давайте поговорим о том, как вещи доступны из массива.

Я утверждаю следующую тестовую ситуацию:

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 ссылка дает иллюзию, что мы делаем это.

0 голосов
/ 16 декабря 2018

Структуры являются типами значений, они копируются при назначении переменной.Когда вы перебираете массив типов значений:

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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...