Какао: каково различие между импортом в заголовке и импортом в основном файле?

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

error expected specifier-qualifier-list before 'someClass'

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

Некоторый поверхностный поиск с помощью Google поднялся, ответ "никогда не импортируют заголовки в заголовочном файле", и это - то, где совет останавливается.

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

7
задан gargantuan 15 December 2009 в 13:46
поделиться

3 ответа

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

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


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

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


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

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


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

10
ответ дан 6 December 2019 в 07:51
поделиться

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

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

Путем перемещения импорта в файл реализации и прямого объявления классов и типов в вашем заголовок (с использованием @class ,

0
ответ дан 6 December 2019 в 07:51
поделиться

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

За предотвращением импорта заголовков в заголовки стоят две цели: улучшение времени инкрементной сборки и избежание циклических зависимостей. Если вы импортируете Ah в Bh и импортируете Bh в Ch , тогда каждый раз, когда вы меняете что-либо в Ah , вам нужно перекомпилировать Cm , даже если Cm не использует ничего из того, что определено в Ah . Это может привести к действительно ужасному и ненужному оттоку сборки, особенно если у вас есть заголовки, которые часто меняются (как это часто бывает на ранних этапах разработки).

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

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

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

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

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

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

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

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

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

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

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

вы должны включить их.

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

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

вы должны включить их.

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

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

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

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

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

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

13
ответ дан 6 December 2019 в 07:51
поделиться