根据我的理解,在ClassA需要包含ClassB标头,而ClassB需要包含ClassA标头以避免任何循环包含的情况下,应该使用前向类声明。我还理解#import是一个简单的ifndef,因此include只发生一次。

我的问题是:什么时候使用#import,什么时候使用@class?有时如果我使用@class声明,我看到一个常见的编译器警告,如下所示:

警告:接收端FooController是转发类,对应的@interface可能不存在。

我真的很想理解这一点,而不是仅仅删除@class forward-declaration并抛出一个#import来沉默编译器给我的警告。


当前回答

我的问题是这样的。什么时候使用#import,什么时候使用@class?

简单的回答:当存在物理依赖时,使用#import或#include。否则,使用正向声明(@class MONClass, struct MONStruct, @protocol MONProtocol)。

以下是一些常见的身体依赖的例子:

任何C或c++值(指针或引用不是物理依赖项)。如果您将CGPoint作为ivar或属性,编译器将需要看到CGPoint的声明。 你的超类。 你使用的方法。

有时如果我使用@class声明,我看到一个常见的编译器警告,如下所示: “警告:接收端‘FooController’是转发类,对应的@interface可能不存在。”

The compiler's actually very lenient in this regard. It will drop hints (such as the one above), but you can trash your stack easily if you ignore them and don't #import properly. Although it should (IMO), the compiler does not enforce this. In ARC, the compiler is more strict because it is responsible for reference counting. What happens is the compiler falls back on a default when it encounters an unknown method which you call. Every return value and parameter is assumed to be id. Thus, you ought to eradicate every warning from your codebases because this should be considered physical dependence. This is analogous to calling a C function which is not declared. With C, parameters are assumed to be int.

The reason you would favor forward declarations is that you can reduce your build times by factors because there is minimal dependence. With forward declarations, the compiler sees there is a name, and can correctly parse and compile the program without seeing the class declaration or all of its dependencies when there is no physical dependency. Clean builds take less time. Incremental builds take less time. Sure, you will end up spending a little more time making sure the all the headers you need are visible to every translation as a consequence, but this pays off in reduced build times quickly (assuming your project is not tiny).

如果您使用#import或#include来代替,则会向编译器抛出大量不必要的工作。您还引入了复杂的头依赖项。你可以把它比作一个蛮力算法。当您使用#import时,会拖入大量不必要的信息,这需要大量内存、磁盘I/O和CPU来解析和编译源代码。

ObjC is pretty close to ideal for a C based language with regards to dependency because NSObject types are never values -- NSObject types are always reference counted pointers. So you can get away with incredibly fast compile times if you structure your program's dependencies appropriately and forward where possible because there is very little physical dependence required. You can also declare properties in the class extensions to further minimize dependence. That's a huge bonus for large systems -- you would know the difference it makes if you have ever developed a large C++ codebase.

因此,我的建议是尽可能使用forward,然后在有物理依赖的地方使用#import。如果您看到警告或其他暗示身体依赖的警告,请全部修复。修复方法是在实现文件中使用#import。

在构建库时,您可能会将一些接口分类为一组,在这种情况下,您将#导入引入物理依赖的库(例如#import <AppKit/AppKit.h>)。这可能会引入依赖关系,但是库维护者通常可以根据需要为您处理物理依赖关系——如果他们引入了一个特性,他们可以将其对构建的影响最小化。

其他回答

向前声明只是为了防止编译器显示错误。

编译器会知道你在头文件中声明的类的名称。

我看到很多“这样做”,但我没有看到任何“为什么”的答案。

那么:为什么在头文件中使用@class,而在实现中使用#import呢?一直使用@class和#import会使你的工作加倍。除非你使用继承。在这种情况下,您将为单个@class #import多次。然后,如果您突然决定不再需要访问某个声明,您必须记得从多个不同的文件中删除。

由于#import的性质,多次导入同一个文件不是问题。 编译性能也不是真正的问题。如果是的话,我们就不会在几乎每个头文件中都使用#importing Cocoa/Cocoa.h之类的代码了。

当我发展的时候,我脑中只有三件事,这三件事从来不会给我带来任何问题。

导入超类 导入父类(当您有子类和父类时) 在项目外部导入类(比如在框架和库中)

对于所有其他类(我的项目中的子类和子类),我通过forward-class声明它们。

查看关于ADC的Objective-C编程语言文档

在定义类|类接口一节中,它描述了为什么这样做:

@class指令最大限度地减少了编译器和链接器看到的代码量,因此是给出类名正向声明的最简单方法。由于简单,它避免了导入仍然导入其他文件的文件时可能出现的潜在问题。例如,如果一个类声明了另一个类的静态类型实例变量,并且它们的两个接口文件相互导入,那么两个类都不能正确编译。

常见的做法是在头文件中使用@class(但仍然需要#import父类),在实现文件中使用#import。这样可以避免任何圆形夹杂物,而且效果很好。