@class против #import

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

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

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

Было бы очень @class это понимать, а просто удалять декларацию @class forward и бросать @class #import чтобы отключить предупреждения, которые дает мне компилятор.

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

предупреждение: приемник «MyCoolClass» – это class вперед, и соответствующий @interface может не существовать

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

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

Например

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

 MyCoolClass *myObject; 

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

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

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

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

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

Посмотрите документацию по языку Objective-C Programming на ADC

В разделе «Определение classа | Class Interface описывает, почему это делается:

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

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

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

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

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

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

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

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

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

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

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

Иногда, если я использую объявление @class, я вижу общее предупреждение о компиляторе, такое как: «предупреждение: приемник« FooController »- это class вперед, а соответствующий @interface может не существовать».

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

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

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

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

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

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

Я вижу много «Сделаю это так», но я не вижу никаких ответов на вопрос «Почему?».

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

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

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

 @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 в .m файле (создать объект и использовать его функцию и переменные).

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

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

резюме статьи

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Пример:

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

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

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

Просто попробуйте это, импортируйте Ah в Bh и Bh в Ah. Не будет никаких проблем или жалоб, и это тоже сработает.

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

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

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

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

Подумайте о #import как copy-paste.

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

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

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

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

 // DroneSearchField.h #import  @class DroneSearchField; @protocol DroneSearchFieldDelegate @optional - (void)DroneTextFieldButtonClicked:(DroneSearchField *)textField; @end @interface DroneSearchField : UITextField @end 
  • Как использовать C ++ с Objective-C в XCode
  • дублирующие символы для архитектуры i386 clang
  • Какао / objective-C: Лучшая практика для анализа XML-документа?
  • "F" после числа / float в Objective-C / C
  • Смешивание Objective-C и C ++
  • Получение NSDecimalNumber из определенной в локали строки?
  • Каков наилучший способ поместить c-struct в NSArray?
  • Механизм отправки сообщения Objective C
  • Зачем писать 1000 000 000 как 1000 * 1000 * 1000 в C?
  • Обратный текст NSString
  • POST multipart / form-data с Objective-C
  • Давайте будем гением компьютера.