Подтвердить что ты не робот

Недостатки Objective-C ++?

Я пишу большой проект для iOS в Objective-C ++. В основном я использую Objective-C для пользовательского интерфейса и других API Apple и С++ для внутренней обработки звука и другой обработки информации. Мне было интересно узнать о недостатках смешивания Objective-C и С++.

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

4b9b3361

Ответ 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, обновление проекта часто не поддерживается.