В каких случаях значимые типы в Swift хранятся не на стеке
Что проверяют
Понимаете ли вы, что деление «структуры на стеке, классы в куче» это упрощение. Вопрос отделяет тех, кто читал про управление памятью, от тех, кто заучил правило.
Как отвечать
Значимый тип действительно чаще живёт на стеке, но уезжает в кучу в нескольких ситуациях:
- является свойством класса: экземпляр класса лежит в куче, значит и вложенная структура хранится там же;
- захвачен убегающим замыканием: замыкание переживает вызов функции, поэтому захваченные значения переносятся в контекст в куче;
- хранит внутри себя ссылочный буфер:
Array,String,DictionaryиSetэто структуры, но их содержимое лежит в куче и работает по схеме копирования при записи. Сама структура на стеке, данные нет; - обёрнут в существующий тип или протокол: значение, положенное в переменную типа протокола или
Any, при недостатке места во встроенном буфере упаковывается в кучу; - лежит в косвенном перечислении (
indirect enum) или в рекурсивной структуре данных, где размер нельзя вычислить заранее; - упаковано в ссылочный контейнер: помещение структуры в объект класса или в
NSObject-совместимый API.
Полезно добавить практический вывод: копирование при записи означает, что копия массива стоит дёшево, пока никто не пишет, и вызывает реальное копирование при первой мутации, если ссылок больше одной. Проверяется это через isKnownUniquelyReferenced в собственных типах с буфером.
Где спотыкаются
Утверждают, что структуры всегда на стеке.
Считают, что большая структура автоматически уезжает в кучу: размер сам по себе не решает, но большие структуры дороже копировать.
Не знают, что оптимизатор компилятора может убрать выделение памяти вовсе, если значение не покидает функцию.