Ответ
Структуры — это value-типы и по умолчанию хранятся в стеке. Однако они могут быть размещены в куче косвенно, если становятся частью reference-типа или когда этого требует семантика копирования при записи (Copy-on-Write, CoW).
Основные случаи:
-
Когда структура является свойством класса: Классы (reference-типы) хранятся в куче, и все их свойства, включая структуры, размещаются там же.
struct Point { var x, y: Int } class Box { var point: Point // Структура `Point` хранится в куче внутри экземпляра `Box` } -
Когда структура захватывается escaping-замыканием: Замыкание, которое может пережить контекст, в котором оно создано, хранится в куче и захватывает все используемые в нем значения.
var storedClosure: (() -> Void)? func captureStruct() { let myStruct = MyStruct() storedClosure = { print(myStruct) } // `myStruct` захватывается и попадает в кучу } -
При работе с коллекциями, использующими CoW: Стандартные типы, такие как
Array,Dictionary,String, хранят свои буферы в куче. При помещении в них структуры, она также окажется там.struct Item { var id: Int } var array = [Item(id: 1)] // Буфер массива и элемент `Item` находятся в куче.
Вывод: Сама по себе структура не «попадает» в кучу напрямую. Это происходит, когда она становится частью другого объекта, который управляется подсчетом ссылок (класс, escaping-замыкание, CoW-буфер).
Ответ 18+ 🔞
Да ты посмотри, какая интересная хуйня выходит! Вот все кричат: "Структуры — value-типы, они в стеке, быстрые, легкие, красота!" А потом такой чувак приходит, пишет код, и у него всё ебётся нахуй, потому что он не понимает, куда на самом деле эта структура может свалиться.
Смотри, вот тебе три главных пиздеца, когда твоя милая, стековая структурка внезапно оказывается в куче, и начинается подсчёт ссылок, как у какой-то шлюхи-классицы.
Случай первый — классический подлян.
Ты создал структуру Point, она вроде как твоя, чистая, в стеке. А потом ты её, блядь, суёшь в класс. А класс — это же ссылочный тип, он живёт в куче, как богатый дядя в особняке. И всё, что у него внутри, тоже там живёт. Твоя Point теперь не девочка с района, а содержанка в золотой клетке кучи.
struct Point { var x, y: Int }
class Box {
var point: Point // Всё, пидор, поздравляю. Твой `Point` теперь в куче, потому что сам `Box` в куче.
}
Случай второй — коварный, как змея подколодная.
Замыкания. Ну, создал ты локальную структурку в функции, подумал — щас вызовется и освободится. Ан нет! Ты её захватил в escaping-замыкание, которое может тебя пережить. Куда это замыкание девается? Правильно, сука, в кучу! И тащит за собой всё, что захватило. Твоя структурка теперь в заложниках у этого замыкания, в самой гуще кучи.
var storedClosure: (() -> Void)?
func captureStruct() {
let myStruct = MyStruct() // Родилась в стеке, чистая.
storedClosure = { print(myStruct) } // Ёбта! Попала в плен. Теперь она в куче, вместе с этим замыканием.
}
Случай третий — массовый, системный. Массивы, словари, строки. Они же все умные, используют Copy-on-Write (CoW). То есть пока ты один, они хранят буфер где? В куче, блядь! Положил ты свою структурку в массив — всё, приехали. Она теперь лежит в этом общем буфере, который в куче. И пока массив не скопируют, она там и будет тлеть.
struct Item { var id: Int }
var array = [Item(id: 1)] // Кажется, просто массив? А буфер-то у массива в куче! И твой `Item` теперь там же.
Вывод, ёпта: Сама по себе структура не стремится в кучу, как порядочная девушка не стремится в бордель. Но если её завербовать в банду ссылочных типов (классов, замыканий, CoW-коллекций) — всё, пиши пропало. Она становится частью их системы, их чёрной дыры в куче. Так что думай, куда ты свою структуру суёшь, а то оптимизация твоя нахуй улетит, сама не заметишь как.