这与编译源文件的顺序有关。您可能已经知道在定义之前不能调用方法(请参见下面的伪代码):
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 类上方,一切都很好。
但是,如果您不想这样做,或者更重要的是,如果两个对象之间存在循环关系怎么办?让我们通过添加对Ship 所在的当前BoatYard 的引用来增加该对象图的复杂性:
@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];
}
这不起作用,并且会失败并显示一条错误消息,指出 Ship 是前向声明。一旦你#import "Ship.h",那么你将能够创建对象的实例。