Могут ли value-типы (структуры, перечисления) в Swift храниться в куче?

Ответ

Да, могут. Хотя value-типы по умолчанию размещаются в стеке, существует несколько сценариев, при которых они попадают в кучу:

  1. Когда value-тип является свойством класса. Классы — reference-типы, их экземпляры всегда размещаются в куче, а значит, и все их свойства (включая структуры и перечисления) хранятся там же.
  2. Когда value-тип захвачен escaping-замыканием. Если замыкание, захватившее value-тип, само сохраняется в куче (например, передано в асинхронную задачу), то захваченные значения также будут размещены в куче.
  3. При использовании unsafe-операций (например, withUnsafeMutablePointer), которые могут явно работать с памятью в куче.

Пример:

class Container {
    var storedStruct: MyStruct // Хранится в куче, так как является свойством класса
}

struct MyStruct {
    var value: Int
}

let container = Container()
container.storedStruct = MyStruct(value: 42) // Экземпляр MyStruct теперь находится в куче

Почему это важно: Понимание этого механизма помогает избежать неверных предположений о производительности и предсказать поведение при работе с замыканиями и классами.

Ответ 18+ 🔞

А, ну это, блядь, классика! Все эти умники с порога заявляют: "Структуры — в стеке, классы — в куче, и точка, ёпта!" А потом такой выходит на сцену, блядь, Герасим-свистопердец и говорит: "Му-му". То есть нихуя не так, блядь!

Да, могут, сука. Хоть по дефолту эти value-типы и юзают стек, как нормальные пацаны, жизнь, блядь, вносит свои коррективы, в рот меня чих-пых. Вот тебе три пиздёжных сценария, когда твоя структурка отправится в эту самую кучу, как последняя мартышлюшка:

  1. Когда она — свойство какого-нибудь класса. Классы — это же reference-типы, ёпта, их экземпляры всегда сидят в куче, как в тюрьме. А раз так, то и всё их барахло, включая твои любимые структуры, болтается там же. Принцип "кто с кем ебётся, тот от того и сосёт" в действии, блядь.
  2. Когда её, дуру, захватило escaping-замыкание. Если замыкание, которое ухватило твою структурку за жопу, само сбежало в кучу (например, его отправили в асинхронную задачу), то и захваченные значения потащат за собой, волоком. Ничего не попишешь.
  3. При использовании unsafe-операций. Ну это уже для полных долбоёбов, которые играются с withUnsafeMutablePointer и прочей чёрной магией, явно указывая памяти, где ей лежать. Тут уж сам напросился, блядь.

Смотри, как это выглядит на практике, ёбана:

class Container {
    var storedStruct: MyStruct // Эта структурка уже обречена на кучу, ибо свойство класса. Пиздец ей.
}

struct MyStruct {
    var value: Int
}

let container = Container()
container.storedStruct = MyStruct(value: 42) // Всё, приехали. Экземпляр MyStruct теперь не в стеке, а в этой самой куче, блядь.

А нахуй это знать-то? А затем, чувак, чтобы не быть тем самым мудаком, который думает, что раз он везде юзает struct, то у него летающая производительность. А потом херак — и всё тормозит, потому что структуры понабежали в кучу, как тараканы на пельмени. Предсказывать поведение, понимать, где что лежит, и не обосраться с замыканиями и классами — вот в чём, блядь, сила.