标题#import与@class

1 iphone macos objective-c

可能重复:
@class与#import

在.h文件中,您可以使用添加要查看的类(不知道这是什么正确的术语)

#import "SomeClass.h"
Run Code Online (Sandbox Code Playgroud)

或者改为使用

@class SomeClass;
Run Code Online (Sandbox Code Playgroud)

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

Ali*_*are 11

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

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

后果:

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

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

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

唯一的例外是当你的类的声明继承另一个类,或者它声明它符合给定的@protocol(如委托协议等)时,因为在这种特殊情况下,编译器需要你对#import父类的整个定义class或@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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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