В каких случаях структура хранится в куче в Swift
Что проверяют
Понимаете ли вы, что место хранения определяется не видом типа, а контекстом, в котором живёт значение. Следом обычно спрашивают про existential container и про то, почему копирование большой структуры бывает дорогим.
Как отвечать
Начните с поправки к мифу: value type означает семантику копирования, а не «всегда на стеке». Локальная структура, которая не убегает из области видимости, обычно лежит на стеке или в регистрах. В кучу она попадает в таких случаях:
- свойство класса: структура хранится внутри экземпляра класса, а он в куче;
- захват замыканием:
@escaping-замыкание копирует захваченное значение в свой контекст в куче, а изменяемую переменную компилятор выносит в отдельный бокс, чтобы замыкание и функция видели одно значение; - existential (
any Protocol,Any): у контейнера встроенный буфер на три машинных слова. Значение, которое в него не помещается, размещается в куче, а контейнер хранит ссылку; - элементы коллекций:
Array,Dictionary,Setдержат элементы в буфере в куче с copy-on-write; indirect enum: рекурсивный случай хранится в боксе в куче.
Отдельно скажите про вложенные ссылки. Сама структура может жить на стеке, но её поля String, Array или ссылка на класс указывают в кучу. Короткие строки до 15 байт UTF-8 помещаются в саму String, длинные уходят в буфер. Каждое копирование такой структуры увеличивает счётчики ссылок, поэтому структура с десятком ссылочных полей копируется заметно дороже, чем кажется.
Аллокации видно в Instruments, шаблон Allocations. Компилятор при оптимизации иногда убирает бокс и оставляет значение на стеке, так что точная картина зависит от сборки.
Где спотыкаются
Утверждают, что inout копирует структуру в кучу. inout означает семантику «скопировать внутрь и записать обратно», а на практике компилятор обычно передаёт адрес, никакой кучи тут нет.
Говорят, что Any это ссылочный тип. Это экзистенциальный контейнер, и куча появляется только для значений крупнее встроенного буфера.
Считают, что замена класса на структуру автоматически убирает ARC. Если внутри лежат ссылочные поля, retain и release никуда не деваются.