Swift 4.2) Мутация массива структуры с помощью for_in/forEach против доступа по индексу

Я пытаюсь изменить элемент структуры в массиве. Я обнаружил, что вы можете сделать это путем доступа (итерации) к структуре по индексу, но вы не можете этого сделать, если используете цикл for in или forEach{}.

struct Person
{
  var age = 0
  var name = "James"
}

var personArray = [Person]()
personArray += [Person(), Person(), Person()]


personArray.forEach({$0.age = 10}) // error: "Cannot assign to property: '$0' is immutable"

for person in personArray { 
  person.age = 10 // error: "Cannot assign to property: 'person' is a 'let' constant"
}


for index in personArray.indices {
  personArray[index].age = 10 // Ok
}

Может кто-нибудь объяснить?

1 ответ

Как указано в других ответах, вы не можете мутировать в for-in петля или в .forEach метод.

Вы можете использовать последнюю формулировку, которая будет краткой и лаконичной:

for index in personArray.indices {
    personArray[index].age = 10
}

Или измените оригинал personArray целиком:

personArray = personArray.map { person in 
    var person = person // parameter masking to allow local mutation
    person.age = 10
    return person
}

Обратите внимание, что второй вариант может показаться менее эффективным, поскольку он создает новый экземпляр Personкаждый раз, но Swift, кажется, хорошо оптимизирован для этих случаев. Профилировщик времени сообщил, что работа второго варианта с массивом 1 000 000 выполняется почти в 2 раза быстрее.Personс.

Вот бонус, если вам действительно нужен мутирующий аналог для .forEach метод, используйте расширение на MutableCollection:

extension MutableCollection {
    mutating func mutateEach(_ body: (inout Element) throws -> Void) rethrows {
        for index in self.indices {
            try body(&self[index])
        }
    }
}

Это оболочка для мутации массива, эквивалентная вашей первой формулировке, вы можете использовать ее как предполагалось:

personArray.mutateEach { $0.age = 10 }

В Swift структура является типом значения. В цикле for или foreach элемент person является значением, и если бы он был изменяемым, вы бы изменяли только копию оригинала, а не оригинал, как вы намеревались.

В отличие от класса класс является ссылочным типом и в цикле каждый элемент является ссылкой на оригинал. Обновление этого ссылочного значения теперь обновляет оригинал

Измените ваше определение Person на класс вместо struct, и оно будет работать как положено. Для полного объяснения см. https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html

Другие вопросы по тегам