2009-12-15 2 views
7

Я понятия не имею, почему, но иногда мне удалось исправить некоторые ошибки компиляции, в первую очередьКакао: В чем разница между импортом в заголовке и импортом в основной файл?

error expected specifier-qualifier-list before 'someClass' 

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

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

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

ответ

10

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

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


Update: я собирался обновить свой ответ, чтобы ответить на некоторые последующие вопросы, но Rob Napier has a good follow-up.

+1

Как насчет 'typedef' и 'протоколов'? – Joost

+0

Вы бы не узнали о учебнике или образце кода, который использует это? – gargantuan

13

Джон дает хороший совет, но вот немного больше фона на whys, wherefores и exceptions.

Для предотвращения импорта заголовков в заголовки есть две цели: улучшенные инкрементные времена сборки и избежание круговых зависимостей. Если импортировать 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 вы используете. Так что да, вы должны включить их.

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

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

+0

Большое спасибо за это. – gargantuan

0

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

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

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

Смежные вопросы