标题#import与@class

时间:2011-09-11 13:31:24

标签: iphone objective-c macos

  

可能重复:
  @class vs. #import

在.h文件中,您可以使用

添加要查看的类(不知道这是什么正确的术语)
#import "SomeClass.h"

或改为使用

@class SomeClass;

我尝试了两种方法,但它们都有效。有什么不同?我应该使用其中一种方法而不是其他方法吗?什么是最佳做法?

3 个答案:

答案 0 :(得分:11)

#import包含源代码中标头的内容。 因此,也导入了导入标题中的每个声明。

@class仅向编译器声明给定的类存在,但不导入标头本身。它被称为前向声明,因为您只向编译器声明该类存在之前详细定义它(告诉它实现了哪些方法等等)

后果:

  • #import文件中使用.m时,如果修改了标头,则会在下次编译时触发.m #import文件的重新编译。相反,如果您使用@class,则.m不依赖于标头,如果修改了标头,则不会重新编译.m文件。
  • 使用@class也可以避免交叉导入,例如如果A类引用了B类而B类引用了A类,那么在同一时间内你不能在Ah中的Bh #import "A.h"#import B.h(它将是一个“import infinite loop”)
  • 使用@class只声明一个类存在,并且不告诉编译器该类响应的方法。

这就是为什么通常最好的做法是在引用类A 的头文件(.h)中使用@class A转发声明类,只是为了让编译器知道“A”是一个已知类,但不需要在实现(.m)文件中了解更多,#import "A.h",以便您可以在A类的对象上调用方法你的源文件。

除了避免导入循环外,这还可以避免在不需要的情况下重新编译文件,从而减少编译时间。

唯一的例外是当类的声明继承另一个类,或者它声明它符合给定的@protocol(如委托协议等)时,因为在这种特殊情况下,编译器需要您要#import父类的整个定义或@protocol(以了解您的类是否正确符合此给定的协议)。


MyClassA.h

// Tells the compiler that "MyClassB" is a class, that we will define later
@class MyClassB; // no need to #import the whole class, we don't need to know the whole definition at this stage

@interface MyClassA : NSObject {
    MyClassB* someB; // ok, the compiler knows that MyClassB is a class, that's all it needs to know so far
}
-(void)sayHello;
-(void)makeBTalk;
@end

MyClassB.h

@class MyClassA; // forward declaration here too
// anyway we couldn't #import "MyClassA.h" here AND #import "MyClassB.h" in MyClassA.h as it would create an unsolvable import loop for the compiler
@interface MyClassB : NSObject {
    MyClassA* someA; // ok, the compiler knows that MyClassA is a class, that's all it needs to know so far
}
-(void)talk;
-(void)makeABePolite;
@end

MyClassA.m

// import MyClassB so that we know the whole definition of MyClassB, including the methods it declares
#import "MyClassB.h" // thus we here know the "-talk" method of MyClassB and we are able to call it

@implementation MyClassA
-(void)sayHello { NSLog(@"A says Hello"); }
-(void)makeBTalk {
  [someB talk];
  // we can call the 'talk' method because we #imported the MyClassB header and knows this method exists
}
@end

MyClassB.m

// import MyClassA so that we know the methods it declares and can call them
#import "MyClassA.h"
@implementation MyClassB
-(void)talk { NSLog(@"B is talking"); }
-(void)makeABePolite {
  [someA sayHello];
  // we can call this because we #import MyClassA
}
@end

PS:请注意,如果这是一个最佳实践,我知道很多开发人员(包括我自己有时^^){。1}}他们的.h文件中需要的标头,而不是只有前进-declare它使用#import ...这是一个坏习惯 - 或者因为这些开发人员不知道这些微妙之处 - 不幸的是你会在现有代码中遇到。

答案 1 :(得分:0)

使用@class称为前向声明。由于通常您不需要知道.h文件中类的具体细节,因此通常只需要这些。

转发声明可防止您进入导入特定.h文件的情况,该文件表示要导入另一个.h文件,该文件表示再次导入原始.h文件,依此类推。

答案 2 :(得分:0)

@class转发声明允许您使接口的行为类似于接口。含义:声明您的代码。

但这并不意味着您可以省略#import声明。您刚刚将责任转移到导入并使用它的实现。

基本上,由于您没有在当前标头中导入任何其他标头,因此可以将其视为性能提升。

重要说明:当您使用代理时,情况并非如此。 如果您正在使用委托,则必须具有适当的#import语句,以便编译器知道该类将实现哪些委托方法。

您可能还想查看以下SO问题:@class vs. #import