Это связано с порядком компиляции исходных файлов. Возможно, вы уже знаете, что не можете вызвать метод до его определения (см. Ниже псевдокод):
var value = someMethod();
function someMethod()
{
...
}
Это может вызвать ошибку во время компиляции, поскольку someMethod () еще не определен. То же самое относится и к классам. Классы компилируются один за другим компилятором.
Итак, если вы представляете, что все классы помещаются в гигантский файл перед компиляцией, вы, возможно, уже сможете увидеть проблему. Давайте посмотрим на Ship
и BoatYard
класс:
@interface BoatYard : NSObject
@property (nonatomic, retain) Ship* currentShip;
@end
@interface Ship : NSObject
@property (nonatomic, retain) NSString* name;
@property (nonatomic, assign) float weight;
@end
Еще раз, поскольку класс Ship
еще не определен, мы еще не можем обратиться к нему. Решение этой конкретной проблемы довольно просто; измените порядок компиляции и скомпилируйте. Я уверен, что вы знакомы с этим экраном в XCode:
Но знаете ли вы, что вы можете перетаскивать файлы вверх и вниз в списке? Это меняет порядок компиляции файлов. Поэтому просто переместите класс Ship
над классом BoatYard
, и все будет хорошо.
Но что, если вы не хотите этого делать или, что более важно, что, если между двумя объектами существует круговая связь? Давайте увеличим сложность этой диаграммы объекта, добавив ссылку на текущий BoatYard
, в котором находится Ship
:
@interface BoatYard : NSObject
@property (nonatomic, retain) Ship* currentShip;
@end
@interface Ship : NSObject
@property (nonatomic, retain) BoatYard* currentBoatYard;
@property (nonatomic, retain) NSString* name;
@property (nonatomic, assign) float weight;
@end
О, дорогой, теперь у нас проблема. Эти два не могут быть скомпилированы бок о бок. Нам нужен способ сообщить компилятору, что класс Ship * действительно существует. И именно поэтому ключевое слово @class
так удобно.
Чтобы выразить это в терминах непрофессионала, вы говорите: "Поверь мне, мужик, Ship
действительно существует, и ты это скоро увидишь". Чтобы сложить все вместе:
@class Ship;
@interface BoatYard : NSObject
@property (nonatomic, retain) Ship* currentShip;
@end
@interface Ship : NSObject
@property (nonatomic, retain) BoatYard* currentBoatYard;
@property (nonatomic, retain) NSString* name;
@property (nonatomic, assign) float weight;
@end
Теперь компилятор знает, как он компилирует BoatYard
, что вскоре появится определение класса Ship
. Конечно, если этого не произойдет, компиляция все равно будет успешной.
Однако все ключевое слово @class
сообщает компилятору о том, что класс скоро появится. Это , а не замена #import
. Вы все равно должны импортировать файл заголовка, иначе у вас не будет доступа ни к одному из внутренних компонентов класса:
@class Ship
-(void) example
{
Ship* newShip = [[Ship alloc] init];
}
Это не может работать и завершится ошибкой с сообщением об ошибке, в котором говорится, что Корабль является предварительным объявлением. После того, как вы #import "Ship.h"
, вы сможете создать экземпляр объекта.