Когда value type начинает храниться на куче вместо стека в Swift
Что проверяют
Понимаете ли вы, что «структуры на стеке» это упрощение, и знаете ли конкретные случаи.
Как отвечать
Значимый тип хранится там, где объявлен, а не всегда на стеке. В куче он оказывается в следующих случаях:
- является полем класса. Экземпляр класса лежит в куче, и все его поля вместе с ним;
- является элементом коллекции. Буфер массива, множества или словаря выделяется в куче, значит и структуры внутри него живут там;
- захвачен убегающим замыканием. Захваченный контекст должен пережить кадр стека, поэтому размещается в куче;
- используется в асинхронной функции и переживает точку приостановки: локальные переменные переезжают в объект состояния;
- упакован в контейнер протокола. Значение, положенное в переменную типа протокола или в универсальный тип, помещается в специальный контейнер. Он вмещает три машинных слова, и если структура больше, содержимое выносится в кучу;
- перечисление с указанием косвенности или рекурсивное по природе;
- любая структура, содержащая ссылочные поля, сама может лежать на стеке, но её поля участвуют в подсчёте ссылок, и накладные расходы на управление памятью никуда не деваются.
Отсюда главный вывод, который и хотят услышать: преимущество значимых типов не в стеке, а в семантике копирования и в отсутствии подсчёта ссылок для простых случаев. Как только структура содержит строку или массив, счётчики снова работают.
Практический совет: держать структуры небольшими. Крупная структура копируется целиком при каждой передаче, а при упаковке в контейнер протокола ещё и выделяется в куче.
Проверить решения компилятора можно инструментами анализа, но в повседневной работе достаточно понимать перечисленные правила.
Где спотыкаются
Утверждают, что структуры всегда на стеке.
Не знают про размер контейнера протокола и про вынос в кучу.
Считают, что структура со строкой внутри не создаёт нагрузки на управление памятью.
Делают большие структуры ради «быстроты» и получают дорогое копирование.