Ответ 1
Это действительно ожидаемое поведение - и связано с тем, что Array
в Swift (как и многие другие коллекции в стандартной библиотеке) является типом значения с семантикой копирования при записи. Это означает, что его базовый буфер (который хранится косвенно) будет скопирован после мутации (и, как оптимизация, только когда на него нет уникальной ссылки).
Когда вы перебираете Sequence
(например, массив), будь то с помощью forEach(_:)
или стандарта for in
, итератор создается из makeIterator()
последовательности makeIterator()
, а метод next()
повторяется применяется для последовательного создания элементов.
Вы можете думать об итерации последовательности так:
let sequence = [1, 2, 3, 4]
var iterator = sequence.makeIterator()
// 'next()' will return the next element, or 'nil' if
// it has reached the end sequence.
while let element = iterator.next() {
// do something with the element
}
В случае Array
в качестве итератора используется IndexingIterator
который будет перебирать элементы данной коллекции, просто сохраняя эту коллекцию вместе с текущим индексом итерации. Каждый раз, когда вызывается next()
, базовая коллекция подписывается индексом, который затем увеличивается, пока не достигнет endIndex
(вы можете увидеть его точную реализацию здесь).
Поэтому, когда вы пришли к мутированию массива в цикле, его базовый буфер не имеет уникальных ссылок, так как итератор также имеет на него представление. Это заставляет копию буфера, который затем использует myCollection
.
Итак, теперь есть два массива - тот, который перебирается, и тот, который вы мутируете. Любые дальнейшие мутации в цикле не будут вызывать другую копию, пока в буфере myCollection
остаются уникальные ссылки.
Следовательно, это означает, что совершенно безопасно изменять коллекцию с семантикой значения при ее перечислении. Перечисление будет повторяться по всей длине коллекции - полностью независимо от того, какие мутации вы делаете, так как они будут сделаны в копии.