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 / GLES | Metal (MoltenVK как мост) |
| Симулятор/эмулятор | Android эмулятор (x86/x86_64) | iOS симулятор (x86_64/arm64) |
Практические рекомендации и стек
- Начните с ядра на CMake: корректно настройте toolchains для NDK и Xcode.
- Делите проект: core (C/C++) + platform glue (≤30% кода). Пишите тонкие адаптеры: JNI‑обёртки и Objective‑C++ мосты.
- Используйте переносимые библиотеки: Abseil, fmt, protobuf/cereal, vcpkg или conan для зависимостей.
- Тестируйте на реальных устройствах (средний + флагман) и профилируйте отдельно: Android Profiler и Instruments.
- Для 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‑кодом, автоматизируйте сборку и профилирование под обе платформы — это сэкономит время и упростит поддержку.