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

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

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

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


当前回答

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

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

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

其他回答

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

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

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

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

编译器只有在编译器需要知道其实现的情况下才会报错。

Ex:

这可以是,如果你要从中派生你的类或者 如果你要将该类的一个对象作为成员变量(尽管很少)。

如果你只是把它用作指针,它不会抱怨。当然,您必须在实现文件中#import它(如果您正在实例化该类的对象),因为它需要知道实例化对象的类内容。

注意:#import和#include不一样。这意味着没有所谓的循环导入。Import是一种请求,要求编译器查看特定文件以获取某些信息。如果该信息已经可用,编译器将忽略它。

试试这个,在B.h中导入A.h,在A.h中导入B.h。不会有任何问题或抱怨,它也会正常工作。

何时使用@class

只有当你不想在你的头文件中导入头文件时,你才可以使用@class。在这种情况下,你甚至不关心这个类是什么。在这种情况下,您甚至可能还没有该类的头文件。

例如,您正在编写两个库。一个类,我们称之为A,存在于一个库中。该库包含来自第二个库的标头。该头文件可能有一个指针a,但同样可能不需要使用它。如果库1还不可用,如果使用@class,库B将不会被阻塞。但是如果您想导入A.h,那么库2的进程就会被阻塞。

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

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

另一个优点:快速编译

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