Каков наилучший способ организовать исходный код большого приложения какао в Xcode? - PullRequest
5 голосов
/ 08 июля 2011

Вот что я ищу:

Я бы хотел разделить части функциональности на модули или компоненты какого-либо рода, чтобы ограничил видимость других классов, чтобы предотвратить доступ каждого класса к каждому другому классу , что со временем приводит к коду спагетти.

Например, в Java и Eclipse я использовал бы пакеты и помещал каждый пакет в отдельный проект с четко определенной структурой зависимостей.

Вещи, которые я рассмотрел:

  1. Использование отдельных папок для исходных файлов и использование групп в Xcode :
    • Плюсы: просто сделать, настройка Xcode практически не требуется
    • Минусы: нет разделения функций во время компиляции, т. Е. Доступ ко всему только один оператор #import
  2. Использование каркасов :
    • Плюсы: Фреймворковый код не может получить доступ к классам доступа вне фреймворка. Это обеспечивает инкапсуляцию и разделяет вещи
    • Минусы: управление кодом затруднительно, если вы работаете с несколькими Frameworks одновременно. Каждый Framework - это отдельный проект Xcode с отдельным окном
  3. Использование плагинов :
    • Плюсы: Подобно Frameworks, код плагина не может получить доступ к коду других плагинов. Чистое разделение во время компиляции. Источник плагина может быть частью того же проекта Xcode.
    • Минусы: не уверен. Это может быть путь ...

Исходя из вашего опыта, что бы вы выбрали для разделения вещей, имея возможность редактировать все источники в одном проекте?

Edit:

Ответы [ 4 ]

9 голосов
/ 08 июля 2011

Я работал над некоторыми крупными проектами Mac (> 2M SLOC в моем последнем из 90 файлов xcodeproj), и вот мои мысли по управлению ими:

  • Избегайте динамических нагрузок, таких как Frameworks, Bundles или dylibs, если вы фактически не разделяете двоичные файлы между группами. Как правило, они создают больше сложности, чем решают по моему опыту. Кроме того, они не легко переносятся на iOS, что означает поддержку нескольких подходов. Хуже всего то, что наличие большого количества динамических библиотек увеличивает вероятность включения одних и тех же символов в два раза, что приводит к всевозможным сумасшедшим ошибкам. Это происходит, когда вы напрямую включаете некоторый «вспомогательный» класс в несколько библиотек. Если он включает в себя глобальную переменную, ошибки потрясающие, поскольку разные потоки используют разные экземпляры глобальной переменной.

  • Статические библиотеки - лучший выбор во многих, если не в большинстве случаев. Они разрешают все во время сборки, позволяя разбирать код в вашем C / C ++ и другие оптимизации, невозможные в динамических библиотеках. Они избавляются от «эй, это загружается в моей системе, но не в клиентской» (когда вы используете неправильное значение для пути платформы). Нет необходимости иметь дело со слайдами при вычислении номеров строк из стеков сбоя. Они ловят повторяющиеся символы во время сборки, сохраняя много часов отладочной боли.

  • Разделение основных компонентов на отдельные xcodeproj. Правда, подумайте, что здесь означает «майор». Моего продукта из 90 проектов было слишком много. Просто проверка зависимостей может стать очень нетривиальным упражнением. (Xcode 4 может улучшить это, но я покинул проект прежде, чем мы когда-либо смогли заставить Xcode 4 надежно построить его, поэтому я не знаю, насколько хорошо он это сделал в конце.)

  • Отделяйте публичные заголовки от частных. Вы можете сделать это со статическими библиотеками так же хорошо, как с Frameworks. Поместите публичные заголовки в другой каталог. Я рекомендую каждому компоненту иметь для этой цели свой публичный каталог include.

  • Не копировать заголовки. Включите их непосредственно из общедоступного каталога include для компонента. Копирование заголовков в общее дерево кажется отличной идеей, пока вы не сделаете это. Затем вы обнаруживаете, что вы редактируете копию, а не реальную, или вы редактируете реальную копию, но фактически не копируете ее. В любом случае, это делает развитие головной болью.

  • Используйте файлы xcconfig, а не панель сборки. Панель сборки сведет вас с ума в подобных крупных проектах. У моих, как правило, такие строки:


common="../../common"
foo="$(common)/foo"
HEADER_SEARCH_PATHS = $(inherited) $(foo)/include

  • В вашем пути к общедоступному заголовку укажите собственное имя пакета. В приведенном выше примере путь к главному заголовку будет common/foo/include/foo/foo.h. Дополнительный уровень кажется болезненным, но это настоящая победа при импорте. Затем вы всегда импортируете так: #import <foo/foo.h>. Держит все очень чисто. Не используйте двойные кавычки для импорта публичных заголовков. Используйте только двойные кавычки для импорта частных заголовков в ваш собственный компонент.

  • Я не решил лучший путь для Xcode 4, но в Xcode 3 вы всегда должны связывать свои собственные статические библиотеки, добавляя проект в качестве подпроекта и перетаскивая цель «.a» на шаг ссылки , Это гарантирует, что вы свяжете тот, который создан для текущей платформы и конфигурации. Мои действительно огромные проекты еще не смогли конвертировать в Xcode 4, поэтому у меня пока нет четкого мнения о том, как лучше там.

  • Избегайте поиска пользовательских библиотек (флаги -L и -l на шаге ссылки).Если вы создаете библиотеку как часть проекта, воспользуйтесь приведенным выше советом.Если вы предварительно создали его, добавьте полный путь в LD_FLAGS.Поиск библиотек включает в себя несколько удивительных алгоритмов и усложняет понимание.Никогда не помещайте предварительно созданную библиотеку в шаг ссылки.Если вы добавляете предварительно встроенный libssl.a в шаг ссылки, он фактически добавляет параметр -L для пути, а затем добавляет -lssl.В соответствии с правилами поиска по умолчанию, даже если на панели сборки отображается libssl.a, вы фактически ссылаетесь на систему libssl.so.Удаление библиотеки приведет к удалению -l, но не -L, так что вы сможете найти причудливые пути поиска.(Я ненавижу панель сборки.) Сделайте это вместо этого в xcconfig:


LD_FLAGS = "$(openssl)/lib/libssl.a"

  • Если у вас есть стабильный код, который используется несколькимипроекты, и при разработке этих проектов вы никогда не будете связываться с этим кодом (и не хотите, чтобы исходный код был доступен), тогда может оказаться разумным подход Framework.Если вам нужны плагины, чтобы избежать загрузки большого количества ненужного кода (и вы действительно не будете загружать этот код в большинстве случаев), тогда пакеты могут быть разумными.Но в большинстве случаев для разработчиков приложений один большой исполняемый файл, связанный из статических библиотек, является лучшим подходом IMO.Общие библиотеки и платформы имеют смысл, только если они на самом деле shared во время выполнения.
4 голосов
/ 08 июля 2011

Мое предложение будет:

  1. Использовать рамки. Они являются наиболее легко используемым артефактом сборки из перечисленных вами опций, и то, как вы описываете структуру того, что вы пытаетесь достичь, очень похоже на создание набора фреймворков.

  2. Используйте отдельный проект для каждого Framework. Вы никогда не сможете заставить компилятор применять требуемые ограничения доступа, если все будет записано в одном проекте. И если вы не можете заставить компилятор применять его, тогда удачи в том, чтобы ваши разработчики сделали это.

  3. Обновление до XCode4 (если вы этого еще не сделали). Это позволит вам работать над несколькими проектами в одном окне (очень похоже на то, как это делает Eclipse), не смешивая проекты. Это в значительной степени устраняет недостатки, которые вы перечислили в опции Frameworks.

И если вы ориентируетесь на iOS, я настоятельно рекомендую вам создавать реальных фреймворков , в отличие от поддельных, которые вы получаете с помощью метода bundle-hack, если вы не строите реальные фреймворки уже.

2 голосов
/ 08 июля 2011

Мне удалось сохранить здравомыслие в работе над моим проектом, который вырос за последние месяцы до довольно большого (количество классов), заставляя себя усердно практиковать Model-View-Control (MVC), плюс здоровое количествокомментарии и необходимый источник контроля (subversion, затем git).

В общем, я наблюдаю следующее:

"Модель" Классы, которые сериализуют данные (не имеет значения, откуда ивключая «состояние» приложения) в классе Objective C 1, подклассе из NSObject или пользовательских «модельных» классах, которые наследуются от NSObject.Я выбрал Objective-C 1.0 больше для совместимости, поскольку это самый низкий общий знаменатель, и я не хотел застрять в будущем при написании «модельных» классов с нуля из-за зависимости функций Objective-C 2.0.

Классы представления находятся в XIB с версией XIB, установленной для поддержки самого старого набора инструментов, который я должен поддерживать (поэтому я могу использовать предыдущую версию Xode 3 в дополнение к Xcode 4).Я склонен начинать с Apple, предоставившей Cocoa Touch API и фреймворки, чтобы извлечь выгоду из любой оптимизации / улучшения, которую Apple может представить по мере развития этих API.

Классы контроллеров содержат обычный код, который управляет отображением / анимацией представлений (программно, а такжеиз XIBs) и сериализацию данных из классов «модели».

Если я обнаружу, что повторно использую класс несколько раз, я изучу рефакторинг кода и оптимизацию (измеряемую с помощью инструментов) в то, что я называю "служебные "классы, или как протоколы.

Надеюсь, это поможет, и удачи.

0 голосов
/ 08 июля 2011

Это во многом зависит от вашей ситуации и ваших собственных предпочтений.

Если вы кодируете «правильные» объектно-ориентированные классы, то у вас будет структура классов с методами и переменными, скрытыми от других классов, где это необходимо.,Если ваш проект не огромен и не состоит из сотен различных различимых модулей, то, вероятно, достаточно просто сгруппировать классы и ресурсы в папки / группы в XCode и работать с ним таким образом.

Если у вас действительно есть огромный смыслпроект с легко различимыми модулями, тогда непременно создайте структуру.Я хотел бы предложить, однако, что это действительно будет необходимо, только если вы используете один и тот же код в разных приложениях, и в этом случае создание фреймворка / дополнительного проекта будет хорошим способом для эффективного копирования кода между проектами.Практически во всех других случаях это, вероятно, будет просто излишним и намного более сложным, чем необходимо.

Ваша последняя идея, кажется, представляет собой смесь первых двух.Плагины (как я понимаю, вы описываете - скажите, если я ошибаюсь) - это просто отдельные классы в одном проекте?Это, вероятно, лучший способ, и его следует делать (в определенной степени) в любом случае.Если вы создаете функциональность для рисования графиков (например), вам следует выделить новую папку / группу и запустить в ней свои классы и функциональные возможности, включив только эти классы в основное приложение, где это необходимо.

Позвольте мне поставитьэто так.Нет никаких причин для этого ... но, даже если только для вашего здравого смысла - или для удобства поддержки вашего кода - вы всегда должны стремиться сгруппировать все в описательные группы / папки.

...