Какао: Какая разница между импортом в заголовке и импортом в основном файле? - PullRequest
7 голосов
/ 15 декабря 2009

Понятия не имею почему, но иногда мне удавалось исправить некоторые ошибки компиляции, в частности

error expected specifier-qualifier-list before 'someClass'

путем перемещения #import "someClass.h" из файла .h в файл .m. Это также сработало с несколькими другими проблемами, с которыми я столкнулся, которые (загадочным образом с моей точки зрения) были связаны с заголовками.

Некоторое беглое гугление привело к ответу «никогда не импортируйте заголовки в заголовочный файл», и на этом совет прекращается.

Либо я полностью придумал это, либо я взял привычку откуда-то, но я подумал, что заголовок - это то место, где должны быть импортированы заголовки. Ясно, что нет, но кто-нибудь может объяснить мне, почему это так, и каков предпочтительный способ импорта заголовков?

Ответы [ 3 ]

13 голосов
/ 15 декабря 2009

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

Существует две цели, позволяющие избежать импорта заголовков в заголовки: улучшенное время инкрементной сборки и предотвращение циклических зависимостей. Если вы импортируете A.h в B.h и импортируете B.h в C.h, то каждый раз, когда вы что-либо изменяете в A.h, вам придется перекомпилировать C.m, даже если C.m не использует ни одного из вещи, определенные в A.h. Это может привести к действительно ужасному и ненужному оттоку сборки, особенно если у вас часто меняются заголовки (как обычно на ранних стадиях разработки).

Первая цель похвальна, но для небольших проектов, кого это волнует? У вас есть четырехъядерный Pro, и полная сборка занимает пару минут, верно? Но вам все равно придется беспокоиться о второй проблеме: циклических зависимостях. A.h класс ссылок B и B.h класс ссылок A. На самом деле это может происходить довольно часто и может невинно закрасться в систему. Объект коллекции может ссылаться на тип объектов, которые он содержит, а объекты могут ссылаться на тип объекта коллекции. Все, что требуется, это одна ссылка из-за какого-то метода, который принимает или возвращает этот тип. Если у вас есть заголовки, импортирующие другие заголовки, вероятность того, что это произойдет, быстро приближается к единице. Вы получите рекурсивный импорт, и ошибки во время компиляции могут быть ошеломляющими. «Я знаю , что typdef определен! Он тут же! Он импортирован!» Но он еще не был проанализирован, когда вы импортировали этот заголовок. Это то, что вызывает вашу ошибку выше.

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

Некоторые заголовки, которые вы должны импортировать. Ваш суперкласс, конечно. Файлы, которые определяют @protocol, который вы реализуете или typedef, который вы используете. Так что да, вы должны включить их.

А как насчет системных заголовков? Ну, они никогда не будут вызывать отток, и, очевидно, они не будут вызывать рекурсивный импорт, поэтому они в порядке. Я не рекомендую людям использовать @class предварительные объявления для вещей в системных заголовках. Это создает дополнительную работу для пользователя вашего заголовка без значения. Для обеспечения надлежащей гигиены заголовков, пожалуйста, не забудьте заключить системные заголовки в <угловые скобки>, а ваши заголовки - в «кавычки».

Так что это не тривиальный вопрос, а простое правило: избегайте импорта пользовательских заголовков в другие пользовательские заголовки всякий раз, когда компилятор позволит вам.

10 голосов
/ 15 декабря 2009

Если вы не наследуете от включаемого класса, заголовки не следует включать в заголовки. Если вам необходимо включить объект в качестве переменной интерфейса, вам следует использовать @class директива вместо; это сообщит компилятору, что идентификатор ссылается на класс.

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

<Ч />

Обновление: Я собирался обновить свой ответ, чтобы ответить на некоторые более поздние вопросы, но Роб Нейпир имеет хорошее продолжение .

0 голосов
/ 15 декабря 2009

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

Это не только шумно, но и приводит к проблемам, когда у вас есть ссылки на классы. Каждый класс должен быть импортирован раньше другого, что приводит к тому, что сначала импортируется один класс, а затем невозможно найти тип другого класса. Это приводит к загадочному сообщению об ошибке, которое вы опубликовали, что в основном означает, что компилятор не может найти тип someClass.

Перемещая импорт в файл реализации и объявляя вперед классы и типы в заголовке (используя @class, @protocol и т. Д.), Вы можете избежать этих проблем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...