@class vs. #import

Насколько я понимаю, следует использовать объявление прямого класса в случае, если ClassA необходимо включить заголовок ClassB, а ClassB должен включить заголовок ClassA, чтобы избежать каких-либо циклических включений. Я также понимаю, что #import - это простой ifndef, так что включение происходит только один раз.

Мой вопрос таков: когда человек использует #import, а когда он использует @class? Иногда, если я использую объявление @class, я вижу общее предупреждение компилятора, например следующее:

warning: receiver 'FooController' is a forward class and corresponding @interface may not exist.

Очень хотелось бы это понять, вместо того, чтобы просто удалить предварительное объявление @class и добавить #import, чтобы заставить замолчать предупреждения, которые компилятор дает мне.

вопрос задан 27.11.2008
Coocoo4Cocoa
17640 репутация

16 ответов


  • 738 рейтинг

    Если вы видите это предупреждение:

    предупреждение: получатель 'MyCoolClass' является классом пересылки и соответствующий @interface может не существовать

    вам нужно #import файл, но вы можете сделать это в вашем файле реализации (. m) и используйте объявление @class в заголовочном файле.

    @class (обычно) не устраняет необходимость в #import файлах, он просто перемещает требование ближе к тому месту, где информация полезна.

    для примера

    Если вы скажете @class MyCoolClass, компилятор знает, что он может увидеть что-то вроде:

    MyCoolClass *myObject;
    

    Не нужно беспокоиться ни о чем, кроме MyCoolClass, являющегося допустимым классом, и он должен зарезервировать место для указателя на него (на самом деле, просто указатель). Таким образом, в вашем заголовке @class достаточно 90% времени.

    Однако, если вам когда-либо понадобится создать или получить доступ к членам myObject, вам нужно сообщить компилятору, что это за методы. На этом этапе (предположительно в вашем файле реализации) вам понадобится #import "MyCoolClass.h", чтобы сообщить компилятору дополнительную информацию, помимо «это класс».

    ответ дан Ben Gottlieb, с репутацией 78943, 27.11.2008
  • 179 рейтинг

    Три простых правила:

    • Только #import суперкласс и принятые протоколы в заголовочных файлах (.h файла).
    • #import все классы и протоколы, в которые вы отправляете сообщения при реализации (.m файлов).
    • Форвард декларации для всего остального.

    Если вы делаете предварительное объявление в файлах реализации, то вы, вероятно, делаете что-то не так.

    ответ дан PeyloW, с репутацией 34379, 29.08.2009
  • 110 рейтинг

    Посмотрите документацию по языку программирования Objective C для АЦП

    В разделе об определении класса | Интерфейс класса это описывает, почему это сделано:

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

    Я надеюсь, что это помогает.

    ответ дан Abizern, с репутацией 97378, 27.11.2008
  • 47 рейтинг

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

    Исключение для этого заключается в том, что вы должны #import класс или формальный протокол, который вы наследуете в вашем заголовочном файле (в этом случае вам не нужно будет импортировать его в реализацию).

    ответ дан Marc Charbonneau, с репутацией 38460, 27.11.2008
  • 24 рейтинг

    Распространенной практикой является использование @class в заголовочных файлах (но вам все еще нужно #import суперкласса) и #import в файлах реализации. Это позволит избежать любых круговых включений, и это просто работает.

    ответ дан Steph Thirion, с репутацией 4740, 27.11.2008
  • 24 рейтинг

    Еще одно преимущество: быстрая компиляция

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

    ответ дан vent, с репутацией 720, 9.09.2010
  • 18 рейтинг

    Мой запрос таков. Когда кто-то использует #import и когда он использует @class?

    Простой ответ: Вы #import или #include, когда есть физическая зависимость. В противном случае вы используете предварительные декларации (@class MONClass, struct MONStruct, @protocol MONProtocol).

    Вот несколько общих примеров физической зависимости:

    • Любое значение C или C ++ (указатель или ссылка не является физической зависимостью). Если у вас есть CGPoint в качестве ивара или свойства, компилятору нужно будет увидеть объявление CGPoint.
    • Ваш суперкласс.
    • Метод, который вы используете.

    Иногда, если я использую объявление @class, я вижу общее предупреждение компилятора, например следующее: msgstr "предупреждение: получатель 'FooController' является классом пересылки, и соответствующий @interface может не существовать. "

    Компилятор на самом деле очень снисходителен в этом отношении. Он будет сбрасывать подсказки (такие, как приведенные выше), но вы можете легко испортить свой стек, если проигнорируете их и не сделаете правильно #import. Хотя это должно (IMO), компилятор не обеспечивает это. В ARC компилятор более строг, потому что он отвечает за подсчет ссылок. Что происходит, так это то, что компилятор возвращается к значению по умолчанию, когда встречает неизвестный метод, который вы вызываете. Предполагается, что каждое возвращаемое значение и параметр будут id. Таким образом, вы должны исключить каждое предупреждение из ваших кодовых баз, потому что это следует рассматривать как физическую зависимость. Это аналогично вызову функции C, которая не объявлена. С C параметры предполагаются равными int.

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

    Если вместо этого вы используете #import или #include, вы потратите на компилятор гораздо больше работы, чем необходимо. Вы также вводите сложные зависимости заголовка. Вы можете сравнить это с алгоритмом грубой силы. Когда вы используете #import, вы тянете за собой тонны ненужной информации, которая требует много памяти, дискового ввода-вывода и процессора для анализа и компиляции источников.

    ObjC довольно близок к идеалу для языка на основе C в отношении зависимости, потому что типы NSObject никогда не являются значениями - типы NSObject всегда являются указателями с подсчетом ссылок. Таким образом, вы можете получить невероятно быстрое время компиляции, если вы правильно структурируете зависимости вашей программы и, по возможности, продвигаетесь вперед, потому что требуется очень мало физической зависимости. Вы также можете объявить свойства в расширениях класса для дальнейшей минимизации зависимости. Это огромный бонус для больших систем - вы бы знали разницу, если бы вы когда-либо разрабатывали большую кодовую базу C ++.

    Поэтому я рекомендую использовать форварды, где это возможно, а затем - #import, где есть физическая зависимость. Если вы видите предупреждение или другое, которое подразумевает физическую зависимость - исправьте их все. Исправление к #import в вашем файле реализации.

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

    ответ дан justin, с репутацией 96103, 8.02.2012
  • 11 рейтинг

    Я вижу много «Сделай так», но я не вижу ответов на «Почему? "

    Итак: Почему вы должны @class в своем заголовке и #import только в вашей реализации? Вы удваиваете свою работу, постоянно @class и #import. Если вы не используете наследство. В этом случае вы будете #importing несколько раз для одного @class. Затем вы должны забыть удалить из нескольких разных файлов, если вдруг решите, что вам больше не нужен доступ к объявлению.

    Многократный импорт одного и того же файла не является проблемой из-за природы #import. Компиляция производительности тоже не проблема. Если бы это было так, мы бы не импортировали Какао / Какао. h или подобное почти во всех заголовочных файлах.

    ответ дан Bruce Goodwin, с репутацией 168, 29.06.2011
  • 7 рейтинг

    , если мы сделаем это

    @interface Class_B : Class_A
    

    означает, что мы наследуем Class_A в Class_B, в Class_B мы можем получить доступ ко всем переменным class_A.

    , если мы делаем это

    #import ....
    @class Class_A
    @interface Class_B
    

    здесь мы говорим, что мы используем Class_A в нашей программе, но если мы хотим использовать переменные Class_A в Class_B, мы должны #import Class_A in. m файл (создать объект и использовать его функцию и переменные).

    ответ дан Anshuman Mishra, с репутацией 139, 4.01.2012
  • 5 рейтинг

    для дополнительной информации о зависимостях файлов & amp; #import & amp; @class проверить это:

    http: // qualitycoding. org / file-dependencies / Это хорошая статья

    Краткое содержание статьи

    импорт в заголовочных файлах:

    • # импортируйте суперкласс, который вы наследуете, и протоколы, которые вы реализуете.
    • Форвард-декларация всего остального (если только это не происходит из фреймворка с основным заголовком).
    • Попробуйте устранить все другие #imports.
    • Объявите протоколы в своих собственных заголовках, чтобы уменьшить зависимости.
    • Слишком много предварительных деклараций? У вас большой класс.

    импорт в файлах реализации:

    • Ликвидация неиспользуемых импортных файлов.
    • Если метод делегирует другому объекту и возвращает то, что он получает назад, попробуйте заранее объявить этот объект вместо #importing.
    • Если включение модуля заставляет вас включать уровень за уровнем последовательные зависимости, у вас может быть набор классов, который хочет стать библиотекой Соберите его как отдельную библиотеку с мастером заголовок, так что все может быть введено в виде одного готового куска.
    • Слишком много #imports? У вас большой класс.
    ответ дан Homam, с репутацией 428, 12.08.2013
  • 3 рейтинг

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

    Ваша первая мысль, вероятно, #import это.
    Это может вызвать проблемы в некоторых случаях.

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

    Поэтому вы можете сказать компилятору с @class:

    Я знаю, что вы не знаете этот класс, но он существует. Он будет импортирован или реализован в другом месте.

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

    Вы обычно будете использовать #import в . м и @class в . h файлов.

    ответ дан NSAddict, с репутацией 4737, 7.01.2013
  • 3 рейтинг

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

    1. Импорт супер классов
    2. Импорт родительских классов (если у вас есть дети и родители)
    3. Импорт классов за пределы вашего проекта (например, в рамках и библиотеках)

    Для всех других классов (подклассы и дочерние классы в моем проекте self) я объявляю их через forward-class.

    ответ дан Randy Marsh, с репутацией 5435, 18.05.2011
  • 0 рейтинг

    Это пример сценария, где нам нужен @class.

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

    // DroneSearchField.h
    
    #import 
    @class DroneSearchField;
    @protocol DroneSearchFieldDelegate
    @optional
    - (void)DroneTextFieldButtonClicked:(DroneSearchField *)textField;
    @end
    @interface DroneSearchField : UITextField
    @end
    
    ответ дан Sujananth, с репутацией 121, 27.09.2016
  • 0 рейтинг

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

    Пример:

    1. Это может быть как если вы собираетесь извлечь свой класс из него или
    2. Если вы собираетесь использовать объект этого класса в качестве переменной-члена (хотя и редко).

    Он не будет жаловаться, если вы просто собираетесь использовать его в качестве указателя. Конечно, вам придется #import его в файле реализации (если вы создаете экземпляр объекта этого класса), так как он должен знать содержимое класса для создания экземпляра объекта.

    ПРИМЕЧАНИЕ: #import отличается от #include. Это означает, что нет ничего, что называется круговым импортом. import - это своего рода запрос компилятора на поиск определенного файла для получения определенной информации. Если эта информация уже доступна, компилятор игнорирует ее.

    Просто попробуйте это, импортируйте A. ч в б ч и б ч в А. час Там не будет никаких проблем или жалоб, и это будет работать нормально.

    Когда использовать @class

    Вы используете @class, только если вы даже не хотите импортировать заголовок в свой заголовок. Это может быть случай, когда вам даже не важно знать, что это будет за класс. Случаи, когда у вас может даже не быть заголовка для этого класса.

    Примером этого может быть то, что вы пишете две библиотеки. Один класс, назовем его A, существует в одной библиотеке. Эта библиотека содержит заголовок из второй библиотеки. Этот заголовок может иметь указатель A, но, опять же, может не понадобиться его использовать. Если библиотека 1 еще не доступна, библиотека B не будет заблокирована, если вы используете @class. Но если вы ищете для импорта А. h, то прогресс библиотеки 2 блокируется.

    ответ дан Deepak G M, с репутацией 1408, 18.07.2013
  • 0 рейтинг

    Представьте, что @class говорит компилятору: «Поверьте мне, это существует».

    Думайте о #import как о копи-пасте.

    Вы хотите свести к минимуму количество импортов по ряду причин. Без каких-либо исследований первое, что приходит на ум, - это сокращение времени компиляции.

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

    ответ дан Brandon Mantzey, с репутацией 115, 4.01.2018
  • 0 рейтинг

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

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

    ответ дан karthick, с репутацией 1, 22.06.2012