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

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

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

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


当前回答

另一个优点:快速编译

如果包含头文件,其中的任何更改都会导致当前文件也被编译,但如果类名被包含为@class name,则不会出现这种情况。当然,您需要在源文件中包含头文件

其他回答

如果你试图在头文件中声明一个变量,或者一个属性,你还没有导入,你会得到一个错误,说编译器不知道这个类。

你的第一个想法可能是导入它。 在某些情况下,这可能会导致问题。

例如,如果你在头文件中实现了一堆c方法,或者结构,或者类似的东西,因为它们不应该被多次导入。

因此你可以用@class告诉编译器:

我知道你不知道那门课,但它确实存在。它将被导入或在其他地方实现

它基本上告诉编译器关闭并编译,即使它不确定这个类是否会被实现。

通常在.m文件中使用#import,在.h文件中使用@class。

三个简单的规则:

只在头文件(.h文件)中#导入超类和采用的协议。 #导入所有类和协议,你发送消息到实现(。m文件)。 其他所有东西的前向声明。

如果在实现文件中进行前向声明,则可能会出错。

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

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

常见的做法是在头文件中使用@class(但仍然需要#import父类),在实现文件中使用#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>)。这可能会引入依赖关系,但是库维护者通常可以根据需要为您处理物理依赖关系——如果他们引入了一个特性,他们可以将其对构建的影响最小化。