C/C++ на Android и iOS: общее и где придётся писать по‑разному

Короткий ответ: да — ядро на C/C++ (≈70–80% кода) реально переиспользовать между Android и iOS, но интеграционный слой (NDK/JNI vs Objective‑C++/Swift bridging), графика и сборка требуют платформо‑специфичных решений.

Общее между платформами

C/C++ компилируется в нативный код на обеих платформах, что даёт максимальную производительность для игр, ML и вычислений. Основные общие элементы:

  • Инструменты сборки: CMake или Bazel — один CMakeLists.txt может генерировать проекты для Android NDK и Xcode.
  • Библиотеки: стандартная библиотека (STL), Boost, TBB, OpenGL ES (в ограниченной форме) и переносимые алгоритмы работают без правок.
  • Параллелизм: std::thread, std::mutex, OpenMP или TBB переносятся напрямую.
  • Фреймворки и движки: Unreal, Unity, Filament и др. уже используют этот подход для кроссплатформенного ядра.
  • Тестирование: GoogleTest подходит для обоих окружений; CI — собрать для нескольких ABI и симуляторов/устройств.

Ключевые отличия: интеграция, графика, ABI и сборка

  • Сборка и бинарники:
    • Android: .so (ARM64, armeabi-v7a и т.д.) с NDK (clang). ABI широкая — нужно поддерживать несколько срезов.
    • iOS: .a/.framework или встроенные object-файлы; App Store требует arm64 для релиза, симулятор — x86_64/arm64 Mac‑simulator.
  • Вызовы и биндинги:
    • Android: JNI — C‑style функции, JNIEnv, jni::ScopedLocalRef; сложнее управлять взаимной жизнью объектов.
    • iOS: Objective‑C++ (.mm) позволяет вызывать C++ объекты напрямую из Objective‑C/Swift через мост.

Частая ошибка — хранить JNIEnv или локальные референсы между вызовами. Используйте Scoped/GlobalRef и освобождайте ресурсы вовремя.

  • Графика:
    • Android: Vulkan / OpenGL ES широко доступны.
    • iOS: Metal — нужен либо отдельный рендер‑бэкэнд, либо MoltenVK/Filament для Vulkan‑совместимости.

Для кроссплатформенных игр рассмотрите абстракции (bgfx, Filament) — они скрывают 90% различий в API рендеринга.

  • Отладка и профилирование: LLDB в Android Studio и Xcode; на iOS важна оценка энергопотребления (Instruments).

Сравнение интеграции и сборки

АспектAndroid (NDK)iOS (Xcode)
Бинарник.so (ARM/ARM64/…).a / .framework (arm64)
ВызовыJNI (C)Objective‑C++ (C++ объекты)
ГрафикаVulkan / GLESMetal (MoltenVK как мост)
Симулятор/эмуляторAndroid эмулятор (x86/x86_64)iOS симулятор (x86_64/arm64)

Практические рекомендации и стек

  1. Начните с ядра на CMake: корректно настройте toolchains для NDK и Xcode.
  2. Делите проект: core (C/C++) + platform glue (≤30% кода). Пишите тонкие адаптеры: JNI‑обёртки и Objective‑C++ мосты.
  3. Используйте переносимые библиотеки: Abseil, fmt, protobuf/cereal, vcpkg или conan для зависимостей.
  4. Тестируйте на реальных устройствах (средний + флагман) и профилируйте отдельно: Android Profiler и Instruments.
  5. Для ML используйте TensorFlow Lite (Android) и Core ML (iOS), оптимизируйте ядро на C++ и используйте платформенные интеропы.

Частые ошибки

  • Хранение JNIEnv или Java/Objective‑C объектов после выхода из вызова.
  • Ожидание одинакового поведения OpenGL/Metal без учёта точностей и шейдерных различий.
  • Игнорирование ABI и несовпадения размеров типов (удерживайте static asserts).
  • Недостаточное тестирование на нескольких архитектурах (arm64, armeabi-v7a, симуляторы).

FAQ

  • Сколько кода реально переиспользовать? — Обычно 70–80% для движка/логики; UI и сервисы остаются платформенными.
  • Можно ли использовать Vulkan на iOS? — Да, через MoltenVK, но ожидайте ограничения и накладные расходы.
  • Что проще: JNI или Objective‑C++? — Objective‑C++ обычно чище для работы с C++ объектами; JNI лучше для простых C‑функций.

Итог: выбирайте C/C++ для тяжёлых вычислений и рендеринга; планируйте ясную границу между переносимым ядром и glue‑кодом, автоматизируйте сборку и профилирование под обе платформы — это сэкономит время и упростит поддержку.