Когда создаешь структуру в
Swift, интуитивно кажется, что ее размер должен равняться сумме размеров параметров.
Int - 8 байт,
String - 16,
Bool - 1. Сложил и получил 25. Но на практике компилятор может добавить несколько скрытых байт, и тот же набор полей внезапно займет уже 32 байта. Все дело в выравнивании и пустых вставках между полями.
Как процессор читает память:
Процессор не работает с байтами по одному. Ему удобнее читать данные блоками, размер которых зависит от архитектуры. На 64-битных системах это обычно 8 байт. И важное условие: адрес, по которому лежит значение, должен быть кратен размеру этого значения.
Int должен начинаться с адреса, кратного 8.
String в
Swift занимает 16 байт, но его внутренняя структура такова, что он также требует выравнивания на 8 байт.
Bool может лежать где угодно.
Компилятор об этом знает и автоматически добавляет пустые байты (padding) между полями, чтобы соблюсти выравнивание. Из-за этого порядок объявления полей влияет на итоговый размер структуры.
Два примера - два размера:
Возьмем структуру с тремя полями разных типов:
struct First {
let flag: Bool // 1 байт
let name: String // 16 байт
let count: Int. // 8 байт
}
Поля идут так:
Bool (1 байт), затем
String (16 байт), но чтобы
String начался с адреса, кратного 8, компилятор добавляет 7 пустых байт после
Bool. Затем идет
Int (8 байт), который уже сам по себе выровнен. Сумма: 1 + 7 + 16 + 8 = 32 байта. Конечный адрес структуры тоже должен быть выровнен по максимальному требованию (8 байт), у нас он уже кратен 8 и остается без изменений.
А теперь поменяем порядок:
struct Second {
let name: String // 16 байт
let count: Int. // 8 байт
let flag: Bool // 1 байт
}
String и
Int идут первыми - оба с правильным выравниванием, никакого
padding между ними не нужно.
Bool в конце занимает 1 байт. Итог: 16 + 8 + 1 = 25 байт. Но теперь конечный адрес структуры не кратен 8, поэтому компилятор добавит ещё 7 байт в конец, чтобы выровнять структуру в памяти. Финальный размер - 32 байта.
Вроде бы оба варианта дали 32 байта? Да, но если добавить еще одно маленькое поле, разница станет заметной:
struct Third {
let flag: Bool // 1 байт
let name: String // 16 байт
let flag2: Bool // 1 байт
let count: Int. // 8 байт
}
Здесь после первого
Bool добавляется 7 байт
padding, затем
String, потом
flag2 (1 байт) и перед
Int снова нужно добавить 7 байт
padding. Итого: 1 + 7 + 16 + 1 + 7 + 8 = 40 байт.
А если сгруппировать правильно:
struct Fourth {
let name: String // 16 байт
let count: Int. // 8 байт
let flag: Bool // 1 байт
let flag2: Bool // 1 байт
}
Все крупные поля идут в начале, мелкие - в конце.
Padding нужен только в конце для выравнивания всей структуры: 16 + 8 + 1 + 1 = 26 байт, плюс 6 байт в конце = 32 байта. Экономия 8 байт на каждой структуре.
Почему это важно:
В изолированной структуре разница в несколько байт не критична. Но когда таких структур тысячи в массиве или словаре, лишние байты превращаются в мегабайты лишней памяти. Особенно чувствительно это для кэшей, списков и любых данных, которые живут долго.
Кроме того, понимание выравнивания помогает при оптимизации: иногда достаточно просто переставить поля местами, чтобы структура похудела без потери функциональности.
String,
Int,
Double требуют выравнивания по 8 байт,
Bool и мелкие
enum - по 1. Группируйте большие поля вместе, маленькие - в конец.
Что с этим делать:
Не нужно фанатично переставлять поля везде подряд. Но если вы работаете с большими объемами данных или пишете производительный код, полезно помнить про выравнивание. Инструменты вроде
MemoryLayout помогут проверить реальный размер структуры:
print(MemoryLayout.size) // 26
print(MemoryLayout.stride) // 32
size - реальный размер данных без учета конечного выравнивания,
stride - сколько байт структура реально занимает в памяти с учётом padding.
Вывод:
Размер структуры в
Swift зависит не только от типов полей, но и от порядка их объявления. Компилятор добавляет невидимые байты, чтобы данные лежали в памяти так, как удобно процессору. Знание этих правил позволяет писать более эффективный код, особенно когда речь идет о больших массивах структур. Иногда простая перестановка полей экономит мегабайты памяти без единой строчки дополнительной логики.