Ответ 1
ObjС++ чрезвычайно эффективен - вы можете одновременно выбирать и смешивать функции, необходимые для ваших проблем, и взаимодействовать с C, ObjC и С++. Я использую его много лет. Есть, конечно, несколько предостережений, и хорошо знать о них, чтобы вы могли свести к минимуму проблемы, с которыми вы могли столкнуться:
Компиляция
Время компиляции намного выше, чем ObjC или С++, когда вы начинаете создавать нетривиальные программы.
Существует несколько общих подходов к объявлению ваших типов С++ в типах ObjC:
- Непрозрачные типы
- Передовые декларации
- Вперед объявления с интеллектуальными указателями
- По значению
Я просто затушевываю это, так как это выведено из OP, что вы знакомы с обоими языками. Кроме того, это один из наиболее публично написанных в вводных темах ObjС++.
С учетом типа С++:
class t_thing { public: int a; };
У вас есть несколько способов объявить ваши ивары:
Непрозрачный тип:
@interface MONClass : NSObject { void* thing; } @end
Этого следует избегать. Нехорошо стереть безопасность типов. В двух вариантах вперед будет введена безопасность типов.
Этот вариант совместим с переводами ObjC.
Форвардная декларация:
class t_thing;
@interface MONClass : NSObject { t_thing* thing; } @end
Это лучше, чем непрозрачный тип, но умный указатель еще лучше - довольно очевидно, если вы привыкли писать современные С++.
Этот вариант совместим с преобразованиями ObjC, если ваши типы С++ находятся в глобальном пространстве имен.
Форвардная декларация с использованием интеллектуальных указателей:
class t_thing;
@interface MONClass : NSObject { t_smart_pointer<t_thing> thing; } @end
Это один из лучших, если вы намерены настроить трансляционные брандмауэры (например, использовать PIMPL и пересылать для уменьшения зависимостей). Кроме того, объект ObjC уже осуществляет блокировку и распределение, поэтому не стоит указывать тип С++. Если у вас несколько объявлений, вы можете создать тип оболочки для своей реализации, чтобы уменьшить отдельные распределения.
Этот вариант несовместим с переводами ObjC.
Хорошее время, чтобы напомнить вам, что есть опция компилятора с ObjС++, которую вы должны включить: GCC_OBJC_CALL_CXX_CDTORS
. Что происходит, когда установлен этот флаг? Компилятор создает скрытые методы objc, которые вызывают конструкторы и деструкторы С++ ivars. Если вы используете GCC_OBJC_CALL_CXX_CDTORS
, ваш С++ ivars должен быть по умолчанию конструктивным. Если вы не включили этот флаг, вы должны вручную сконструировать и уничтожить ваши ivars отлично - если вы его дважды сконструируете или не переопределите инициализатор подкласса, тогда вы столкнулись с UB.
По значению:
#include "thing.hpp"
@interface MONClass : NSObject { t_thing thing; } @end
Самая высокая зависимость. Это (в общем) маршрут, который я выбрал, и я сожалею об этом. Я только что переехал, чтобы использовать больше С++ и использовать композицию с интеллектуальными указателями (описанными выше), чтобы уменьшить зависимость.
Этот вариант несовместим с переводами ObjC.
Еще одна вещь о современных компиляторах ObjC: компилятор излагает ваши ivars/структуры типов С++ в двоичном формате. Верьте или нет, это может потреблять много двоичного пространства.
Дело здесь в том, что есть несколько форм, которые может принять программа. Вы можете смешать эти методы, чтобы уменьшить зависимость, и это одно из лучших мест для внедрения брандмауэров зависимости, поскольку ObjC очень динамичен (его методы должны быть экспортированы в один перевод), а для создания объекта требуются выделения, блокировки, введение в подсчет ссылок system - время инициализации для одного объекта уже относительно велико, и реализация всегда будет скрыта.
Если большая часть вашей программы все еще находится в ObjC, и вы хотите сохранить ее таким образом, тогда вам нужно будет использовать форварды типов, объявленных в глобальном пространстве имен или непрозрачных базовых типах, которые вы специализируетесь на специализации через объект factory. Лично я просто использую так много С++, что это был не идеальный вариант, и обертывание реализаций в глобальных типах быстро стало утомительным.
Между тем, поскольку время компиляции велико, инверсия верна: если вы можете сохранить значительную часть своей реализации как С++, тогда вы сэкономите много времени на компиляцию. По этой причине и ARC (ниже) вы можете получить много, сохранив при этом примитивные типы Apple как типы CF, где это возможно, поэтому вы можете продолжать создавать программы на С++ без расширений ObjC.
Синтаксис
У меня редко бывают проблемы, но я строго придерживаюсь своих классов С++:
- Я запрещаю копировать и присваивать по умолчанию.
- Я редко объявляю настраиваемые операторы для типов С++.
Если вы классный на С++, тогда вы можете избежать этой проблемы, но я предпочитаю компилятор поймать глупые ошибки, которые я делаю.
Одна очевидная проблема - разрешение области С++ в сообщении сообщения ObjC. Для этого требуется пробел:
[obj setValue:::func(a)]; // << bad
[obj setValue: ::func(a)]; // << good
читаемость
Одна из проблем, с которыми я столкнулась, заключается в том, что я никогда не нашел форматировщик кода, который хорошо поддерживает синтаксис ObjС++.
Сообщения ObjC
-
Обмен сообщениями ObjC и возврат по значению: вам нужно проверить перед отправкой сообщений
nil
при возврате типов С++ по значению. Если объект, на который вы отправили сообщениеnil
, тогда результат будет обнуленной памятью в современных режимах работы (x86_64 и iOS). Если вы используете этот экземпляр, это поведение undefined. -
Обмен сообщениями ObjC и возврат по ссылке: перед отправкой типов С++ по ссылке необходимо проверить перед отправкой сообщений
nil
. Если объектом сообщения являетсяnil
, то результатом будет поведение undefined (ссылка на0/NULL
).
Чтобы преодолеть проблемы обмена сообщениями ObjC, я обычно использую такую форму:
- (bool)selector:(std::string&)outValue;
где возвращаемое значение является ложным для некоторой внутренней ошибки и истинно для успеха.
тогда вы можете спокойно написать:
if (![obj selector:outString]) { /* bail here */ }
Разное
-
Совместимость с ARC: ObjС++ не подходит для ARC. Основная причина заключается в том, что ARC не следует через модели смешанных объектов. Пример. Если вы попытаетесь поместить член ObjC в тип С++, компилятор отклонит программу под ARC. Это не проблема, потому что MRC просто прост с ObjС++ (при условии, что вы также используете SBRM), но это может быть проблемой для жизни вашей программы.
-
Синтезированные свойства: вам нужно будет определить свои свойства для типов С++.
-
Внешние инструменты: В дополнение к набору инструментов Xcode существует несколько программ, которые хорошо справляются или распознают ObjС++. Текстовые редакторы, IDE, утилиты.
-
Apple Tools: в утилитах Xcode поддержка Xcode для ObjС++ немного ниже. Рефакторинг (недоступен), навигация (улучшена с помощью анализатора clang), изложение (довольно примитивно), ObjС++ может нарушить утилиты IB, обновление проекта часто не поддерживается.