Ответ
Хотя value-типы (структуры, перечисления, кортежи) по умолчанию размещаются в стеке, что обеспечивает быстрый аллокейт и деаллокейт, существует несколько сценариев, когда они или их содержимое оказываются в куче:
1. Захват escaping-замыканием Когда value-тип захватывается escaping-замыканием (которое может быть вызвано позже, чем завершится функция), система вынуждена аллоцировать память в куче, чтобы обеспечить время жизни захваченных значений.
func createEscapingClosure() -> () -> Void {
var localCounter = 0 // Int (value type, изначально в стеке)
let closure = {
localCounter += 1
print(localCounter)
}
// Замыкание `closure` является escaping (возвращается из функции).
// `localCounter` будет перемещен в кучу для совместного использования.
return closure
}
let savedClosure = createEscapingClosure()
// Область видимости функции createEscasingClosure завершилась,
// но captured `localCounter` жив в куче.
savedClosure() // 1
savedClosure() // 2
2. Является свойством reference-типа (класса) Value-типы, хранящиеся как свойства экземпляра класса, размещаются вместе с этим экземпляром в куче.
class MyClass {
var myPoint = CGPoint(x: 0, y: 0) // CGPoint (struct) находится в куче внутри экземпляра MyClass
}
3. Приведение к протокольному типу (Existential Container) Когда value-тип присваивается переменной протокольного типа, система использует existential container, который может хранить небольшие значения внутри себя (inline), но для больших значений (более 3-х слов) использует аллокацию в куче.
protocol Drawable {}
struct LargeStruct: Drawable { // Большая структура (например, > 24 байт)
var data: (Int, Int, Int, Int, Int, Int, Int) = (1,2,3,4,5,6,7)
}
let value = LargeStruct() // Создается в стеке
let boxed: Drawable = value // Может привести к аллокации в куче для хранения
4. Внутренняя реализация "copy-on-write" (CoW) коллекций
Стандартные библиотечные коллекции (Array, String, Dictionary, Set) являются value-типами, но для эффективности хранят свои буферы данных в куче. При копировании копируется только ссылка на буфер. Реальное копирование данных (аллокация нового буфера в куче) происходит только при модификации (write) одной из копий.
var array1 = [1, 2, 3, 4, 5] // Буфер массива аллоцирован в куче
var array2 = array1 // Копируется только ссылка на буфер (дешево)
// Оба массива сейчас указывают на один буфер в куче
array2.append(6) // Только здесь происходит реальное копирование
// (аллокация нового буфера для array2)
Итог: Размещение в куче для value-типов — это оптимизация времени жизни или производительности, необходимая, когда значение должно пережить свою исходную область видимости или когда семантика копирования по значению должна быть реализована эффективно.