Примеры случаев хранения значений на куче и как устроено выделение памяти для массивов
Что проверяют
Понимаете ли вы, что в Swift тип сам по себе не определяет место хранения. Вопрос быстро отделяет заученное «структуры на стеке, классы в куче» от реального понимания, где возникает аллокация и сколько она стоит.
Как отвечать
В куче гарантированно живут экземпляры классов и акторов, замыкания, которые убегают за пределы вызова, и буфер любого типа с динамическим размером. Структура может лежать на стеке, но если она захвачена escaping-замыканием, хранится в поле класса или помещена в контейнер протокольного типа, её содержимое уезжает в кучу вместе с владельцем.
Отдельные случаи:
Array,Dictionary,Set,Stringэто структуры со ссылкой на буфер в куче: само значение маленькое, данные снаружи. У строк есть оптимизация коротких значений, они живут прямо внутри без аллокации;- захваченная замыканием переменная переезжает в отдельную ячейку в куче, поэтому её изменения видны обеим сторонам;
- значение протокольного типа, не влезающее во встроенный буфер, тоже боксируется.
Про массивы: элементы лежат непрерывным блоком, при исчерпании ёмкости выделяется новый блок большего размера и элементы переносятся. Рост геометрический, поэтому append амортизированно константный, но отдельный вызов может стоить копирования всего массива. Если размер известен заранее, reserveCapacity убирает промежуточные аллокации.
Где спотыкаются
Отвечают лозунгом «структуры всегда на стеке». Массив структур, поле класса, захват в замыкании: во всех трёх случаях данные оказываются в куче, и разговор про производительность на этом обрывается.
Путают копирование при записи и рост ёмкости. Первое про то, что копия массива делит буфер с оригиналом до первой записи, второе про исчерпание capacity. Вопросы «почему буфер вдруг скопировался» и «почему append иногда дорогой» имеют разные ответы.
Считают, что подсчёт ссылок снимает все вопросы. Цикл сильных ссылок утекает молча: замыкание, захватившее self и сохранённое в свойстве этого же объекта, держит его вечно. Лечится списком захвата с [weak self] и проверкой в профилировщике памяти.