@class和#import有什么区别

时间:2010-07-29 09:34:55

标签: objective-c

当我使用以下代码编译时,没有错误:

@class RootViewController;
//#import "RootViewController.h"

当我使用以下代码编译时,出现错误:

//@class RootViewController;
#import "RootViewController.h"

“错误:在'RootViewController'之前预期的说明符限定符列表”

我不明白两者之间的区别是什么,因为我在类似的类中使用了#import并且编译时没有错误!

8 个答案:

答案 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指令给编译器足够的预警预期。但是,在实际使用类的接口(创建实例,发送消息)的情况下,必须导入类接口。

http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-TPXREF123

答案 2 :(得分:6)

@class用于避免循环依赖...这可以防止循环引用在一个标题A中导入第二个标题B,其中(B)导入第一个(A)导入第二个(B)等等在无限循环中.... @class通常用于要求编译器在运行时查找其定义...尤其是当它驻留在某个静态库中时。

#import以外的其他作品

See this question

答案 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: - 它定义您可以创建导入类的实例变量并在您的类中使用它。

import: - 它定义您可以访问在所需导入类中声明的变量。

您可以使用指定链接获取更多信息。

http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-TPXREF123

答案 5 :(得分:0)

@class表示尚未声明类RootViewController的定义,但将在运行时定义。我相信它就像在c ++中声明一个外部类。

#import相当于#include。

通过错误消息,我猜你在RootViewController.h里面的某个地方犯了一个错误,比如一个被遗忘的;或类似的东西

答案 6 :(得分:0)

您必须在此处要导入的类中导入此类。这就是你得到错误的原因,但是可以通过@class示例来纠正。

答案 7 :(得分:0)

@class是一个前向声明,一个好的做法是将它们放在.h而不是#import中,以避免循环#import问题。