В каких случаях структура (struct) в Swift хранится не на стеке?

Ответ

Хотя структуры в Swift являются типами-значениями и по умолчанию размещаются на стеке, существуют исключения, когда они попадают в кучу (heap):

  1. При присвоении типу Any или протоколу Происходит упаковка (boxing) в контейнер в куче.

    struct Point { var x, y: Int }
    let anyValue: Any = Point(x: 1, y: 2) // Упаковано в кучу
  2. При захвате escaping-замыканием Если структура захвачена замыканием, которое может пережить текущую область видимости (@escaping), она копируется в кучу.

  3. При использовании модификатора indirect Он явно указывает компилятору хранить член структуры по ссылке, что необходимо для рекурсивных определений.

    indirect struct Node {
        var value: Int
        var next: Node?
    }
  4. Когда структура является свойством класса Поскольку экземпляр класса живет в куче, все его свойства (включая структуры) хранятся там же.

  5. При явной упаковке в ссылочный тип (Boxing) Иногда используется шаблон "Box" для обертки структуры в класс, чтобы передавать ее по ссылке или изменять внутри замыканий.

Почему это важно? Непредвиденное размещение в куче может повлиять на производительность из-за накладных расходов на выделение памяти и подсчет ссылок (для внутреннего контейнера).

Ответ 18+ 🔞

А, ну это классика, блядь! Сидишь такой, думаешь: "Структура — значит стек, быстрая как чёрт, ни тебе ARC, ни тебе проблем". А потом раз — и хуй с горы, она уже в куче болтается, и ты сидишь с охуевшим лицом, спрашивая, что за пиздопроебибна произошла.

Смотри, вот тебе пять причин, почему твоя милая, стековая структурка вдруг решает, что ей в куче раздольнее.

Первый номер, ёпта — когда лезешь в Any или протокол.
Тут компилятор смотрит на тебя, как на идиота, и говорит: "Ну раз ты такой умный, хочу хранить что угодно — получай упаковку!" И твоя структура летит в специальный контейнер в куче, как в тюрьму.

struct Point { var x, y: Int }
let anyValue: Any = Point(x: 1, y: 2) // Всё, приехали. Упаковано в кучу, блядь.

Второй момент — когда её хватает escaping-замыкание.
Если твоё замыкание сбегает (@escaping) и тащит с собой структуру — компилятор не рискует. Он её копирует на хуй в кучу, чтобы она не испарилась, когда стек почистят. Подозрение ебать чувствую к этим escaping-штукам.

Третий случай — явный намёк через indirect.
Это когда ты сам говоришь: "Э, бошка думай, тут рекурсия, храни это по ссылке, а то сломаемся".

indirect struct Node {
    var value: Int
    var next: Node? // Без indirect тут был бы пиздец, а так — всё в куче, красота.
}

Четвёртая причина — если структура стала свойством класса.
Ну тут всё просто: класс живёт в куче, как богатый дядя в особняке. И всё, что у него внутри — даже стопка столовых приборов-структур — лежит там же, в этом особняке. Не на помойке-стеке.

И пятое — явная упаковка в класс (Boxing).
Бывает, самому надо обернуть структуру в класс, чтобы мутить её по ссылкам или менять внутри замыканий. Это как посадить чижа в клетку — живёт теперь в куче, хоть и чиж.

А нахуй это важно?
Да потому что, блядь, нежданное путешествие в кучу — это овердохуища накладных расходов: выделение памяти, подсчёт ссылок у внутреннего контейнера... Всё это жрёт время. Думал, что гоночный болид сделал, а он оказался гружёной фурой, которая ещё и платит за проезд. Волнение ебать!