Ответ
По умолчанию экземпляры структур (value types) размещаются в стеке. Однако они могут быть перемещены в кучу (heap) в следующих случаях:
- Когда структура является свойством класса. Объект класса живет в куче, и все его свойства, включая структуры, размещаются там же.
- Когда структура захватывается escaping-замыканием. Замыкание, которое может пережить контекст, в котором оно создано, и его захваченные значения размещаются в куче.
- При использовании с existential типами (протоколами). Когда структура присваивается переменной типа протокола (например,
var item: SomeProtocol), она упаковывается в existential container, который может использовать кучу. - При работе с небезопасными указателями или внешними функциями, которые явно требуют выделения в куче.
Пример:
// 1. Структура как свойство класса → куча
class Container {
var data: MyStruct // MyStruct размещается в куче вместе с объектом Container
}
// 2. Захват escaping-замыканием → куча
func createEscapingClosure() -> () -> Void {
var capturedStruct = MyStruct(value: 10)
return { // Замыкание может быть вызвано позже
print(capturedStruct.value) // capturedStruct перемещена в кучу
}
}
struct MyStruct {
var value: Int
}
Почему это важно: Размещение в куче влечет накладные расходы на выделение/освобождение памяти и подсчет ссылок (для захваченных значений). Понимание этого помогает оптимизировать производительность, минимизируя неожиданные аллокации в куче.
Ответ 18+ 🔞
Ну, вот смотри, все эти умники кричат: «Структуры — они в стеке, быстро, эффективно, красота!» Ага, щас. Как будто в Свифте всё так просто, блядь. На деле-то эта хуйня может вылезти в кучу, когда ты совсем не ждёшь, и потом сидишь, чешешь репу — откуда просадки перформанса.
Так вот, слушай сюда, основные причины, почему твоя милая, стековая структурка вдруг начинает пастись в куче, как корова на лугу:
-
Пристроилась к классу. Ну, тут всё логично, ёпта. Класс — он как богатый дядя, живёт в куче в своём хороме. И если твоя структура стала его свойством, то она переезжает к нему, на ПМЖ в кучу. Без вариантов. Ты ж не будешь её через забор кидать каждый раз, когда нужно обратиться.
-
Её захапало escaping-замыкание. Вот это, сука, самый подлый случай. Смотри: сделал ты локальную переменную-структуру, передал её в замыкание, а замыкание это — убегающее (escaping). То есть его могут вызвать потом, когда твоя функция уже давно кончилась. И компилятор такой: «Ну, братан, раз уж ты так хочешь, чтобы эта структура жила дольше своего стека, держи — переехала в кучу». И всё, приехали. Теперь у неё там своя маленькая жизнь с подсчётом ссылок.
-
Полезла в протоколы (existential типы). О, это классика! Объявил переменную типа протокола:
var shit: SomeProtocol. И пытаешься запихнуть туда свою структуру. Компилятор хитро так ухмыляется: «А я, блядь, не знаю на этапе компиляции, какого она размера. Может, она большая, может, маленькая. Ладно, заверну в специальную коробочку (existential container), и если не влезет — отправим на склад, то есть в кучу». И отправляет, зараза. -
Прямой приказ. Ну, тут уже сам виноват. Работаешь с небезопасными указателями или какими-нибудь старыми С-шными функциями, которые прямо говорят: «Дай мне память из кучи!». Ну и даёшь, чё уж там.
Вот, смотри, наглядненько:
// 1. Пристроилась к классу — сразу в кучу, как пить дать.
class Container {
var data: MyStruct // MyStruct теперь живёт в куче, вместе с этим Container'ом.
}
// 2. Захапана escaping-замыканием — тоже в кучу, прощай, стек.
func createEscapingClosure() -> () -> Void {
var capturedStruct = MyStruct(value: 10) // Вроде в стеке...
return { // А вот и нет, блядь!
print(capturedStruct.value) // capturedStruct теперь в куче, потому что замыкание может убежать.
}
}
struct MyStruct {
var value: Int
}
А нахуя это знать-то? Да затем, что кучная жизнь — это дорого, ёпта! Аллокация, деаллокация, подсчёт ссылок — это всё оверхеды. Если ты пишешь высокопроизводительный код и думаешь, что хитришь со структурами, а они тебе втихую в куче живут — это пиздец. Понимание этих моментов помогает не наступать на эти грабли и не охуевать потом от профайлера.