当我使用以下代码编译时,没有错误:
@class RootViewController;
//#import "RootViewController.h"
当我使用以下代码编译时,出现错误:
//@class RootViewController;
#import "RootViewController.h"
“错误:在'RootViewController'之前预期的说明符限定符列表”
我不明白两者之间的区别是什么,因为我在类似的类中使用了#import并且编译时没有错误!
答案 0 :(得分:25)
@class
,但您不需要知道有关该类的任何详细信息(其方法,例)。当您确实需要使用类时(即向其发送消息),将使用#import
。
例如,如果您在头文件中声明实例变量,则可以使用@class
声明某种类型的实例变量:
@class MyOtherClass;
@interface MyClass : NSObject
{
MyOtherClass *myIvar;
}
@end
由于您尚未使用myIvar
,因此除了MyOtherClass
类型存在外,您不需要了解任何内容。
然而:
#import "MyOtherClass.h"
- (void)doSomething
{
[myIvar doSomethingElse];
}
在这种情况下,您要将doSomethingElse
邮件发送给myIvar
;编译器需要知道MyOtherClass
的实例定义了这个方法,所以你必须导入头文件,否则编译器会抱怨。
为什么要担心这个?
它主要与依赖关系有关。当您#import
将文件A存入文件B时,文件B在文件A上变为依赖 - 也就是说,如果文件A发生更改,则必须重新编译文件B.如果使用{ {1}}在文件B中,文件B不依赖于文件A,因此当文件A更改时不需要重新编译 - 所以如果你只是声明一个类型而不是实际上依赖于文件的实现A,您可以通过@class
文件A来保存编译时间。
答案 1 :(得分:8)
我决定参考文档,因为我仍然感到困惑:
<强>#进口强>
该指令与#include相同,只是它确保同一个文件永远不会被包含多次。因此,它是首选的,并且在基于Objective-C的文档中的代码示例中用于代替#include。
此约定意味着每个接口文件间接包含所有继承类的接口文件。当源模块导入类接口时,它将获取构建该类的整个继承层次结构的接口。
<强> @class 强>
这样的声明只是使用类名作为类型,并且不依赖于类接口的任何细节(它的方法和实例变量),@ class指令给编译器足够的预警预期。但是,在实际使用类的接口(创建实例,发送消息)的情况下,必须导入类接口。
答案 2 :(得分:6)
@class
用于避免循环依赖...这可以防止循环引用在一个标题A中导入第二个标题B,其中(B)导入第一个(A)导入第二个(B)等等在无限循环中.... @class
通常用于要求编译器在运行时查找其定义...尤其是当它驻留在某个静态库中时。
除#import
以外的其他作品
答案 3 :(得分:6)
基本规则:在头文件中使用@class
,在实现文件中使用#import
。
(但是,您需要#import
您的类的超类。在其他情况下,您还需要在标题中使用`#import。)
#import
不等同于#include
。如果文件多次included
,则每次都会加载它,但是同一文件的#imports
个@class
,它仍然只会加载一次。
因此,使用@class
的主要原因不是为了避免循环依赖,而是为了使编译更快。
以下是必须使用//MYControl.h
@class MYControl; // Must use class
@protocol MYControlDelegate
-(void)control:(MYControl *)control didChangeToState:(UIControlState)state;
@end
@interface MYControl : UIControl
{
id<MYControlDelegate> delegate_;
}
@property (nonatomic, assign) id<MYControlDelegate> delegate;
@end
//MYControl.m
@implementation MYControl
@synthesize delegate = delegate_;
. . .
@class
在这种情况下,没有要导入的内容,因为委托协议在头文件的主类上面声明。但是你仍然需要能够引用尚未声明的主类。那么MYControl
所做的就是让编译器知道有一些叫做@class
的类,并且会在某个时候被定义。 (但不是在运行时。该类将在编译过程中定义。)
编辑:来自Objective-C手册:
因为这样的声明很简单 使用类名作为类型而不是 取决于课程的任何细节 接口(其方法和实例 变量),@ class指令给出 编译器充分预警 期待什么。但是,在哪里 实际使用的是类的接口 (创建实例,发送消息), 必须导入类接口。 通常,接口文件使用 @class声明类,和 相应的实施文件 导入他们的接口(因为它 将需要创建那些实例 课程或发送消息)。
@class指令最小化了 编译器看到的代码量 和链接器,因此是 最简单的前进方式 声明一个类名。存在 简单,它避免了潜在的问题 这可能与导入文件有关 导入其他文件。对于 例如,如果一个类声明了一个 静态类型的实例变量 另一个类,和他们的两个接口 文件互相导入,都不是类 可以正确编译。
请注意,在使用{{1}}处理的一般问题类别中,最后一句中提到了循环性。
答案 4 :(得分:2)
@class: - 它定义您可以创建导入类的实例变量并在您的类中使用它。
您可以使用指定链接获取更多信息。
答案 5 :(得分:0)
@class
表示尚未声明类RootViewController的定义,但将在运行时定义。我相信它就像在c ++中声明一个外部类。
#import
相当于#include。
通过错误消息,我猜你在RootViewController.h里面的某个地方犯了一个错误,比如一个被遗忘的;或类似的东西
答案 6 :(得分:0)
您必须在此处要导入的类中导入此类。这就是你得到错误的原因,但是可以通过@class示例来纠正。
答案 7 :(得分:0)
@class是一个前向声明,一个好的做法是将它们放在.h而不是#import中,以避免循环#import问题。