Если во время обхода Array добавить в него элементы, увидит ли текущий итератор новые элементы?
Нет. Текущий итератор продолжит обход исходного состояния массива и не увидит элементы, добавленные после начала итерации. Сам массив после завершения операции будет содержать добавленные значения.
Array в Swift имеет семантику значения: присваивание и передача массива не должны неожиданно связывать последующие изменения разных переменных. Для эффективной реализации этой модели используется copy-on-write — физическое копирование буфера откладывается до момента изменения при наличии других владельцев.
Такой подход позволяет безопасно использовать снимок массива для итерации без немедленного копирования всех элементов и одновременно сохранять предсказуемое поведение коллекций.
Во время обхода может потребоваться добавить элементы в тот же массив: например, расширить очередь задач или накопить результаты. Если бы итератор автоматически видел добавленные значения, обход мог бы стать неограниченным или зависеть от текущей ёмкости внутреннего буфера.
Неверное предположение о динамическом расширении обхода приводит к ошибкам: можно ожидать обработки новых элементов, хотя они останутся за пределами текущего прохода.
При начале обхода итератор получает собственное значение Array, связанное с текущим буфером элементов. Когда исходный массив изменяется, Swift применяет copy-on-write: если буфер разделяется с итератором, для изменяемого массива создаётся отдельная копия.
Поэтому итератор продолжает читать старый буфер, а переменная массива после добавления ссылается на новый. Добавленные элементы будут доступны при следующем обходе.
Будут напечатаны элементы 1, 2, 3, а затем массив [1, 2, 3, 4]. Это не означает, что каждый итератор любой коллекции обязан работать как снимок: поведение зависит от конкретной коллекции и её семантики. Кроме того, если элементы массива являются ссылочными объектами, итератор не копирует сами объекты; изменение их внутренних свойств может быть видно через уже полученные ссылки.
Предположим, обработчик обходит массив задач и добавляет в него задачи, обнаруженные во время обработки. Вариант с прямым изменением массива формально не расширяет текущий проход, поэтому новые задачи могут остаться необработанными.
Можно повторять обход, пока появляются новые элементы, но это усложняет контроль завершения и может привести к циклу. Можно заранее скопировать или сформировать отдельную очередь, однако это увеличивает расход памяти.
Практичнее явно разделить текущий пакет и очередь новых задач: обработать исходный снимок, затем добавить обнаруженные задачи в следующий пакет. Такое решение не зависит от деталей реализации итератора, явно задаёт границы этапа и предотвращает бесконечное расширение одного прохода.
Обычно копируется значение массива, а не каждый его элемент. Для значимых типов это обеспечивает независимое состояние благодаря copy-on-write, но для ссылочных типов копируются ссылки на те же объекты.
Может увидеть. Если массив содержит экземпляры классов, итератор хранит ссылки на эти экземпляры. Изменение свойства объекта меняет сам объект, поэтому уже полученная итератором ссылка будет наблюдать новое состояние.
Нет. Контракт Collection описывает обход, индексы и операции над элементами, но не обещает одинаковую модель снимка при мутации. Для конкретного типа нужно учитывать его семантику значения или ссылки, правила инвалидирования индексов и документацию API. Безопасный обобщённый алгоритм не должен полагаться на то, что изменение коллекции во время обхода будет незаметно для итератора.