Насколько я понимаю, следует использовать объявление прямого класса в случае, если ClassA необходимо включить заголовок ClassB, а ClassB должен включить заголовок ClassA, чтобы избежать каких-либо циклических включений. Я также понимаю, что #import
это простойifndef
так что включение происходит только один раз.
Мой вопрос таков: когда один использует #import
и когда один использует @class
? Иногда, если я использую @class
объявление, я вижу общее предупреждение компилятора, такое как следующее:
warning: receiver 'FooController' is a forward class and corresponding @interface may not exist.
Очень хотелось бы понять это, вместо того, чтобы просто удалить предварительное @class
объявление и добавить #import
в него, чтобы заглушить предупреждения, которые компилятор дает мне.
источник
Ответы:
Если вы видите это предупреждение:
вам нужен
#import
файл, но вы можете сделать это в файле реализации (.m) и использовать@class
объявление в вашем заголовочном файле.@class
(обычно) не устраняет необходимость в#import
файлах, а просто перемещает требование ближе к тому месту, где информация полезна.Например
Если вы говорите
@class MyCoolClass
, компилятор знает, что он может увидеть что-то вроде:Ему не нужно беспокоиться ни о чем, кроме
MyCoolClass
допустимого класса, и он должен зарезервировать место для указателя на него (на самом деле, просто указатель). Таким образом, в вашем заголовке@class
достаточно 90% времени.Однако, если вам когда-либо понадобится создать или получить доступ
myObject
к членам, вам нужно сообщить компилятору, что это за методы. На этом этапе (предположительно в вашем файле реализации) вам нужно#import "MyCoolClass.h"
сообщить компилятору дополнительную информацию, помимо «это класс».источник
@class
что - то в вашем.h
файл, но забыл#import
ему в ом, пытаетесь получить доступ к методу на@class
объекте эд, и получить предупреждения , как:warning: no -X method found
.Три простых правила:
#import
суперкласс и принятые протоколы в заголовочных файлах (.h
файлах).#import
все классы и протоколы, в которые вы отправляете сообщения (.m
файлы).Если вы делаете предварительное объявление в файлах реализации, то вы, вероятно, делаете что-то не так.
источник
Посмотрите документацию по языку программирования Objective-C на АЦП
Под разделом по определению класса | Интерфейс класса описывает, почему это делается:
Надеюсь, это поможет.
источник
При необходимости используйте предварительное объявление в заголовочном файле и
#import
заголовочные файлы для любых классов, которые вы используете в реализации. Другими словами, вы всегда#import
файлы, которые используете в своей реализации, и если вам нужно сослаться на класс в вашем заголовочном файле, используйте также предварительное объявление.Исключение в том , что вы должны
#import
класс или формальный протокол вы наследующий в файле заголовка (в этом случае вам не нужно импортировать его в реализации).источник
Обычной практикой является использование @class в заголовочных файлах (но вам все еще нужно #import суперкласса) и #import в файлах реализации. Это позволит избежать любых круговых включений, и это просто работает.
источник
#import
«это похоже на директиву C #include, за исключением того, что она гарантирует, что один и тот же файл никогда не включается более одного раза». Таким образом, в соответствии с этим#import
заботятся о круглых включениях,@class
директивы не особенно помогают с этим.Еще одно преимущество: быстрая компиляция
Если вы включаете заголовочный файл, любое изменение в нем также приводит к компиляции текущего файла, но это не тот случай, если имя класса включено как
@class name
. Конечно, вам нужно будет включить заголовок в исходный файлисточник
Простой ответ: вы
#import
или#include
когда есть физическая зависимость. В противном случае, можно использовать вперед деклараций (@class MONClass
,struct MONStruct
,@protocol MONProtocol
).Вот несколько распространенных примеров физической зависимости:
CGPoint
как ivar или свойство, компилятор должен увидеть объявлениеCGPoint
.Компилятор на самом деле очень снисходителен в этом отношении. Он будет сбрасывать подсказки (например, приведенные выше), но вы можете легко испортить свой стек, если проигнорируете их и не сделаете
#import
правильно. Хотя это должно (IMO), компилятор не обеспечивает это. В ARC компилятор более строг, потому что он отвечает за подсчет ссылок. Что происходит, так это то, что компилятор возвращается к значению по умолчанию, когда он встречает неизвестный метод, который вы вызываете. Каждое возвращаемое значение и параметр предполагается равнымid
. Таким образом, вы должны исключить каждое предупреждение из ваших кодовых баз, потому что это следует рассматривать как физическую зависимость. Это аналогично вызову функции C, которая не объявлена. С C параметры предполагаются равнымиint
.Причина, по которой вы бы предпочли предварительные декларации, заключается в том, что вы можете сократить время сборки за счет факторов, поскольку зависимость минимальна. С помощью предварительных объявлений компилятор видит, что есть имя, и может правильно анализировать и компилировать программу, не видя объявления класса или всех его зависимостей, когда нет физической зависимости. Чистые сборки занимают меньше времени. Инкрементные сборки занимают меньше времени. Конечно, в конечном итоге вы потратите немного больше времени на то, чтобы все заголовки, которые вам нужны, были видны для каждого перевода, как следствие, но это быстро окупается за меньшее время сборки (если ваш проект не крошечный).
Если вы используете
#import
или#include
вместо этого, вы тратите на компилятор гораздо больше работы, чем необходимо. Вы также вводите сложные зависимости заголовка. Вы можете сравнить это с алгоритмом грубой силы. Когда ты#import
, вы таскаете тонны ненужной информации, которая требует много памяти, дискового ввода-вывода и процессора для анализа и компиляции источников.ObjC довольно близок к идеалу для языка на основе C в отношении зависимости, потому что
NSObject
типы никогда не являются значениями -NSObject
типы всегда являются указателями с подсчетом ссылок. Таким образом, вы можете получить невероятно быстрое время компиляции, если вы правильно структурируете зависимости вашей программы и, по возможности, продвигаетесь вперед, потому что требуется очень мало физической зависимости. Вы также можете объявить свойства в расширениях класса для дальнейшей минимизации зависимости. Это большой бонус для больших систем - вы бы знали разницу, если бы вы когда-либо разрабатывали большую кодовую базу C ++.Поэтому я рекомендую использовать форвардов, где это возможно, а затем туда,
#import
где есть физическая зависимость. Если вы видите предупреждение или другое, которое подразумевает физическую зависимость - исправьте их все. Исправление#import
в вашем файле реализации.Когда вы создаете библиотеки, вы, вероятно, классифицируете некоторые интерфейсы как группу, и в этом случае вы будете использовать
#import
ту библиотеку, в которой вводится физическая зависимость (например#import <AppKit/AppKit.h>
). Это может привести к зависимости, но сопровождающие библиотеки часто могут обрабатывать физические зависимости по мере необходимости - если они вводят функцию, они могут минимизировать влияние, которое она оказывает на ваши сборки.источник
NSObject types are never values -- NSObject types are always reference counted pointers.
не совсем верно. Блоки бросают лазейку в вашем ответе, просто говоря.Я вижу много "Делай так", но я не вижу ответов на "Почему?"
Итак: почему вы должны @class в вашем заголовке и #import только в вашей реализации? Вы удваиваете свою работу, постоянно @class и #import. Если вы не используете наследство. В этом случае вы будете #importing несколько раз для одного @class. Затем вы должны забыть удалить из нескольких разных файлов, если вдруг решите, что вам больше не нужен доступ к объявлению.
Многократный импорт одного и того же файла не является проблемой из-за природы #import. Компиляция производительности тоже не проблема. Если бы это было так, мы бы не #importing Cocoa / Cocoa.h или тому подобное почти во всех заголовочных файлах.
источник
если мы сделаем это
значит, мы наследуем Class_A в Class_B, в Class_B мы можем получить доступ ко всем переменным class_A.
если мы делаем это
здесь мы говорим, что мы используем Class_A в нашей программе, но если мы хотим использовать переменные Class_A в Class_B, мы должны #import Class_A в файле .m (создать объект и использовать его функцию и переменные).
источник
для дополнительной информации о зависимостях файлов & #import & @class проверьте это:
http://qualitycoding.org/file-dependencies/ Это хорошая статья
краткое содержание статьи
источник
Когда я развиваюсь, я имею в виду только три вещи, которые никогда не вызывают у меня никаких проблем.
Для всех других классов (подклассы и дочерние классы в моем проекте self) я объявляю их через forward-class.
источник
Если вы попытаетесь объявить переменную или свойство в вашем заголовочном файле, который вы еще не импортировали, вы получите ошибку, сообщающую, что компилятор не знает этот класс.
Ваша первая мысль, вероятно,
#import
это.Это может вызвать проблемы в некоторых случаях.
Например, если вы реализуете группу C-методов в заголовочном файле, или структурах, или что-то подобное, потому что они не должны импортироваться несколько раз.
Поэтому вы можете сказать компилятору
@class
:Он в основном говорит компилятору закрыть и скомпилировать, хотя он не уверен, будет ли когда-либо реализован этот класс.
Обычно вы будете использовать
#import
в .m и@class
в .h файлах.источник
Переслать объявление только для того, чтобы компилятор не показывал ошибку.
компилятор будет знать, что есть класс с именем, которое вы использовали в вашем заголовочном файле для объявления.
источник
Компилятор будет жаловаться только в том случае, если вы собираетесь использовать этот класс таким образом, что компилятору необходимо знать его реализацию.
Пример:
Он не будет жаловаться, если вы просто собираетесь использовать его в качестве указателя. Конечно, вам придется #import его в файле реализации (если вы создаете экземпляр объекта этого класса), так как он должен знать содержимое класса для создания экземпляра объекта.
ПРИМЕЧАНИЕ: #import не совпадает с #include. Это означает, что нет ничего, что называется круговым импортом. import - это своего рода запрос компилятора на поиск определенного файла для получения определенной информации. Если эта информация уже доступна, компилятор игнорирует ее.
Просто попробуйте это, импортируйте Ah в Bh и Bh в Ah. Там не будет никаких проблем или жалоб, и это будет работать нормально.
Когда использовать @class
Вы используете @class, только если вы даже не хотите импортировать заголовок в свой заголовок. Это может быть случай, когда вам даже не важно знать, что это будет за класс. Случаи, когда у вас может даже не быть заголовка для этого класса.
Примером этого может быть то, что вы пишете две библиотеки. Один класс, назовем его A, существует в одной библиотеке. Эта библиотека содержит заголовок из второй библиотеки. Этот заголовок может иметь указатель A, но, опять же, может не потребоваться его использование. Если библиотека 1 еще не доступна, библиотека B не будет заблокирована, если вы используете @class. Но если вы хотите импортировать Ah, то прогресс библиотеки 2 будет заблокирован.
источник
Представьте, что @class говорит компилятору: «Поверьте мне, это существует».
Думайте о #import как о копировании-вставке.
Вы хотите свести к минимуму количество импортов по ряду причин. Без каких-либо исследований первое, что приходит на ум, - это сокращение времени компиляции.
Обратите внимание, что когда вы наследуете от класса, вы не можете просто использовать предварительное объявление. Вам нужно импортировать файл, чтобы объявленный вами класс знал, как он определен.
источник
Это пример сценария, где нам нужен @class.
Подумайте, хотите ли вы создать протокол в заголовочном файле, в котором есть параметр с типом данных того же класса, тогда вы можете использовать @class. Пожалуйста, помните, что вы можете также объявлять протоколы отдельно, это только пример.
источник