Всегда ли экземпляр структуры (struct) в Swift размещается в стеке?

«Всегда ли экземпляр структуры (struct) в Swift размещается в стеке?» — вопрос из категории Swift Core, который задают на 23% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Нет, не всегда. Хотя структуры являются типами-значениями (value types) и по умолчанию размещаются в стеке, есть важные исключения, которые приводят к выделению памяти в куче (heap).

Когда структура находится в стеке?

  • Локальные переменные внутри функции или метода.
  • Параметры функций, передаваемые по значению (не inout).
func example() {
    var point = CGPoint(x: 10, y: 20) // Выделяется в стеке кадра функции `example`.
}

Когда структура может оказаться в куче?

  1. Структура захватывается замыканием, которое escaping: Замыкание может пережить контекст, поэтому захваченные значения перемещаются в кучу.

    struct Config { var timeout: Int }
    var storedClosure: (() -> Void)?
    
    func storeClosure() {
        let config = Config(timeout: 5) // Изначально в стеке.
        storedClosure = { print(config.timeout) } // `config` теперь в куче!
    }
  2. Структура является свойством ссылочного типа (класса): Память для всего экземпляра класса выделяется в куче.
    class Container {
        var config = Config(timeout: 5) // `config` лежит внутри памяти `Container` в куче.
    }
  3. Структура содержит ссылочные типы: Сама структура-значение может находиться где угодно (стек/куча), но её свойство-класс всегда ссылается на объект в куче.
    struct Wrapper {
        let object = NSObject() // Экземпляр NSObject — в куче.
    }
  4. Использование withUnsafeMutableBytes и т.п.: Работа с сырыми указателями не меняет расположение, но это низкоуровневая операция.

Ключевой вывод

Расположение структуры определяется контекстом её использования, а не только её определением. Компилятор Swift может оптимизировать размещение (например, используя COW — Copy-on-Write для больших структур), но семантика типа-значения сохраняется.