В Swift две переменные содержат один Array: что происходит с памятью при изменении одной из них?
Array имеет семантику значения: после присваивания обе переменные логически независимы. Swift обычно не копирует буфер сразу, а использует copy-on-write; при изменении одной переменной, если буфер разделяется, создаётся отдельная копия, поэтому другая переменная не меняется.
Коллекциям нужны одновременно предсказуемое поведение значений и приемлемая производительность. Полное копирование массива при каждом присваивании обеспечило бы независимость значений, но сделало бы обычную передачу коллекций дорогой по времени и памяти.
Copy-on-write решает этот компромисс: неизменяемые копии могут временно использовать одно хранилище, а копирование откладывается до первой операции, которая способна изменить данные.
Разработчик может ошибочно считать, что присваивание большого массива всегда немедленно удваивает его память, либо, наоборот, ожидать, что изменение одной переменной изменит и другую. Первое приводит к необоснованной оптимизации, второе — к ошибкам в состоянии приложения.
Важно различать логическую независимость значений и физическое устройство памяти. Пользовательский код должен полагаться на семантику значения, а не на то, в какой именно момент и каким способом реализация выделит буфер.
При присваивании массива Swift передаёт дескриптор его хранилища, а не обязательно все элементы. Пока хранилище не изменяется, несколько значений могут разделять один буфер.
Перед мутацией реализация проверяет, является ли хранилище уникальным для изменяемого значения. Если нет, создаётся новый буфер, элементы копируются в него, и операция выполняется уже над новым хранилищем. Поэтому изменение одной переменной не нарушает значение другой.
Если хранилище уникально, его можно изменить на месте без копирования элементов. Это делает последовательные мутации эффективными, но не превращает Array в ссылочный тип: наблюдаемое поведение по-прежнему соответствует независимым значениям.
Минимальный пример:
До append массивы могут разделять буфер. Во время append при необходимости создаётся отдельное хранилище для first; second продолжает ссылаться на исходные элементы.
Точный момент копирования и детали оптимизации не следует трактовать как публичный контракт для ручного управления памятью. Кроме того, copy-on-write самого массива не делает копии независимыми от мутабельных ссылочных объектов, которые лежат внутри него: элементы-ссылки могут по-прежнему указывать на один объект.
В экранном состоянии хранят большой массив моделей. Для фильтрации создаётся рабочая переменная, после чего в ней удаляются элементы. Вариант с немедленным ручным копированием гарантирует независимость, но может без необходимости занять дополнительную память ещё до первой мутации.
Вариант полагаться на семантику Array и выполнить мутацию рабочей переменной обычно лучше: до изменения буфер может разделяться, а при первой записи копирование произойдёт автоматически. Если модели являются ссылочными объектами и их внутреннее состояние тоже нужно изолировать, одного copy-on-write массива недостаточно — потребуется копировать сами модели или использовать значения-структуры.
Выбранное решение: хранить неизменяемое исходное состояние отдельно, создавать рабочее значение массива и мутировать только его. Результат — исходный массив не меняется, а лишнее копирование откладывается до реальной необходимости.
Нет. Семантически создаётся независимое значение, но физическое копирование обычно откладывается благодаря copy-on-write. Поэтому присваивание может быть дешёвым, пока разделяемое хранилище не потребуется изменить.
Копируется структура массива и его ссылки на элементы, но не сами экземпляры классов. После copy-on-write два массива будут иметь разные буферы, однако соответствующие элементы могут ссылаться на один объект. Изменение свойства такого объекта будет видно через оба массива.
Нельзя гарантировать это на уровне прикладного кода. Передача значения часто позволяет разделять хранилище, но последующая мутация, особенности оптимизации или необходимость сохранить данные могут привести к копированию. Правильный контракт — независимая семантика значения, а не обещание конкретного количества аллокаций.