Какие факторы влияют на размер бинарного файла приложения на Swift?

Ответ

Размер Swift-приложения может быть больше аналогичного на Objective-C из-за особенностей компиляции и безопасности языка.

Основные причины:

  1. Библиотека времени выполнения (Swift Runtime):

    • Не является частью ОС (в отличие от Objective-C runtime).
    • Включается в бинарник приложения, добавляя ~2-5 МБ.
  2. Генерики (Generics) и специализация:

    • Компилятор создает отдельную реализацию для каждой комбинации типов, используемых с дженериком.
      // Для Int и String будут сгенерированы две разные функции
      func process<T>(_ value: T) { ... }
      process(10)      // Специализация для Int
      process("text") // Специализация для String
  3. Код для гарантий безопасности:

    • Проверки границ массивов, переполнения целых чисел, обработка опционалов — весь этот код добавляется в бинарник.
  4. Отладочные сборки (-Onone):

    • Содержат символы для отладки и минимальную оптимизацию, что сильно увеличивает размер.

Способы оптимизации размера:

  • Release-оптимизация: Используйте -Osize (оптимизация по размеру) вместо -O (по скорости).
  • Удаление неиспользуемого кода: Включите DEAD_CODE_STRIPPING = YES и STRIP_STYLE = ALL.
  • Использование App Thinning: (Bitcode, Slicing) для доставки оптимизированного бинарника под каждое устройство.
  • Минимизация динамических фреймворков: Статическая линковка может уменьшить итоговый размер.

Ответ 18+ 🔞

Ну вот, опять про размер приложений заговорили. Смотри, объясняю на пальцах, почему твой Swift-код весит как здоровенный кирпич, а старый добрый Objective-C — как перышко.

Первое и главное — Swift Runtime. Это такая штука, без которой твой Swift-код — просто набор красивых букв. И знаешь, где она живёт? Правильно, в твоём бинарнике, ёпта! В iOS она не вшита в систему, как для Objective-C. Так что готовься отдать лишних 2-5 мегабайт просто за право сказать, что пишешь на модном языке. Ядрёна вошь!

Дальше — генерики. О, это моя любимая песня! Ты думаешь, написал одну умную функцию — и всё, красота? А вот хуй там! Компилятор-то не дурак, он для каждого типа, которым ты воспользуешься, сделает свою отдельную копию. Смотри:

func process<T>(_ value: T) { ... }
process(10)      // Тут он сгенерирует функцию для Int
process("text") // А тут уже совсем другую — для String

И так по кругу. Наиспользуешься этих дженериков — и получишь бинарник размером с хороший роман Толстого, блядь.

Третье — безопасность. А ты как думал? За все эти красивые опционалы, за то, что массив сам тебя по рукам бьёт, если за границы полез, — надо платить! Весь этот охранный код, проверки, страховки — всё это компилятор аккуратненько вставляет в твоё приложение. Хочешь жить без сюрпризов? Плати мегабайтами, чувак.

И наконец, если ты собираешь отладочную сборку (-Onone), то вообще пиши пропало. Там и символы отладочные на каждый чих, и оптимизации никакой. Приложение раздувается, как мыльный пузырь.

Так что делать-то, как не околеть с жиру?

  1. Собирай на релиз правильно. Не просто -O (оптимизация по скорости), а -Osize — чтобы компилятор старался не разогнать код, а ужать его по максимуму.
  2. Выкидывай мусор. В настройках проекта включи DEAD_CODE_STRIPPING = YES и STRIP_STYLE = ALL. Пусть линкер вырежет всё, до чего ты ни разу не дотронулся.
  3. Пользуйся App Thinning. Bitcode, Slicing — это не просто слова для красоты. Это чтобы в App Store каждому устройству прилетал только нужный кусок бинарника, а не целый воз с сеном.
  4. С динамическими фреймворками аккуратней. Иногда статическая линковка может помочь размер уменьшить, потому что линкуется только то, что реально используется, а не вся библиотека целиком.

Вот и весь сказ. Хочешь безопасность и современный синтаксис — будь добр, тащи на себе Swift Runtime и все его защитные механизмы. Волшебства не бывает. Чих-пых тебя в сраку!