【问题标题】:What is the difference between @class and #import@class 和 #import 有什么区别
【发布时间】:2010-07-29 09:34:55
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

  • 在您的情况下,您的 RootViewController.h 文件或它导入的文件之一有问题。也许缺少“;”某处。不幸的是,调试头文件有点困难,因为您经常在另一个文件中遇到错误。使用 @class 时没有收到错误的原因是它没有加载包含错误的文件。详情请参阅我的回答。
  • Objective-C @class vs. #import 的可能重复项

标签: objective-c


【解决方案1】:

@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。如果您使用 @class在文件 B 中,文件 B 不依赖于文件 A,因此当文件 A 更改时不需要重新编译 - 所以如果您只是声明一个类型而不实际依赖于文件 A 的实现,您可以不要#importing 文件A,节省自己的编译时间。

【讨论】:

    【解决方案2】:

    我还是一头雾水,所以决定参考一下文档:

    #import

    该指令与#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 天后才允许我接受答案 - 天知道为什么需要这样做
    【解决方案3】:

    基本规则:在你的头文件中使用@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 的类将在某个时候定义。 (但不是在运行时。类将在编译过程中定义。)

    编辑:来自 Objective-C 手册:

    因为这样的声明简直 使用类名作为类型,不要 取决于班级的任何细节 接口(它的方法和实例 变量),@class 指令给出 编译器足够的预警 会发生什么。然而,在哪里 实际使用了类的接口 (创建的实例,发送的消息), 必须导入类接口。 通常,接口文件使用 @class 声明类,以及 对应的实现文件 导入他们的接口(因为它 将需要创建这些实例 类或向他们发送消息)。

    @class 指令最小化 编译器看到的代码量 和链接器,因此是 最简单的前锋方法 类名的声明。存在 简单,它避免了潜在的问题 导入文件可能附带 导入其他文件。为了 例如,如果一个类声明了一个 的静态类型实例变量 另一个类,以及它们的两个接口 文件相互导入,没有类 可以正确编译。

    请注意,最后一句中提到的循环是使用@class 处理的一般问题中的一个。

    【讨论】:

    • 为什么你说@class 不是为了避免循环依赖,然后提供一个例子,在其中它以这种方式使用呢?在您的示例中,MyControl 类依赖于 MyControlDelegate 协议,MyControlDelegate 依赖于 MyControl
    • 顺便说一句,您对委托类型的变量和属性的声明是错误的。他们应该是这样的:id&lt;MyControlDelegate&gt; delegate_;
    • @JeremyP:关于 ivar/property 声明:谢谢,我已经更正了。关于循环问题,我说过循环引用不是@class 存在的主要原因。相反,@class 的目的是减少 any 类型的引用以加快编译速度。循环引用也可以用其他方式处理,尽管@class 在这方面也很方便。我认为这些陈述反映在我添加到我的答案中的引用中。指出这一点后,没有什么能阻止我展示@class 帮助处理循环引用(在一个文件中)的示例。
    • 我认为你错了。编译速度不是我以前听说过使用@class 的原因。也许你可以提供一个引用。
    • 编译速度是我听说使用@class最大原因。
    【解决方案4】:

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

    除了#import 工作之外

    See this question

    【讨论】:

    • 我不同意@class 主要用于避免循环引用,因为#import 已经处理了这一点。 (试试看!)
    • ok 不同意接受... @class 通常用于要求编译器在运行时查找其定义...尤其是当它驻留在某些静态库中时...
    • @Felixyz:对不起,你错了。 @class 的主要用途是避免@interface 和@protocol 定义的循环依赖。
    • @JeremyP:我说“#import 已经解决了这个问题”是错误的,因为并非总是如此。但是,我认为说@protocol 定义是@class 的“主要用途”有点夸张。例如,在我在回答中添加的引用中,没有提到@protocol。
    • @Felixyz:我不是那个意思。我的意思是@class的使用是为了避免@interfaces的循环依赖,它也被用来避免@protocols的循环依赖,以及两者的组合。
    【解决方案5】:

    @class:- 它定义了你可以创建导入类的实例变量并在你的类中使用它。

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

    您可以使用给定的链接了解更多信息。

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

    【讨论】:

      【解决方案6】:

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

      #import 等价于#include。

      通过错误消息,我猜你只是在 RootViewController.h 中的某个地方犯了一个错误,例如忘记了;或者类似的东西

      【讨论】:

      • 说它“将在运行时定义”是不正确的。该类肯定会在编译期间的某个时候定义。事实上,它可能已经被定义了。 @class 只是说在这个文件的上下文中,我们不会访问该类的任何字段或方法,所以我们只需要知道它的名称,以便我们可以声明指向它的指针。
      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-07-23
          • 2017-04-20
          • 2010-10-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多