我的问题是:什么时候使用 #import,什么时候使用 @class?
简单的答案是:当有物理依赖关系时,您会使用 #import 或 #include。否则,使用前向声明(@class MONClass、struct MONStruct、@protocol MONProtocol)。
以下是一些常见的物理依赖关系示例:
- 任何 C 或 C++ 值(指针或引用不是物理依赖项)。如果您将 CGPoint 作为 ivar 或 property,则编译器需要查看 CGPoint 的声明。
- 您的超类。
- 您使用的方法。
有时,如果我使用 @class 声明,我会看到一个常见的编译器警告,如下所示:"warning: receiver 'FooController' is a forward class and corresponding @interface may not exist."
实际上,编译器在这方面非常宽容。它会给出提示(如上面的提示),但是如果您忽略它们并且没有正确 #import,则很容易崩溃。虽然它应该(在我看来),但编译器并不强制执行。在 ARC 中,编译器更加严格,因为它负责引用计数。发生的情况是,当您调用一个未知方法时,编译器会回退到默认值。每个返回值和参数都被认为是 id。因此,您应该从代码库中清除每个警告,因为这应该被视为物理依赖关系。这类似于调用未声明的 C 函数。对于 C,参数被认为是 int。您会倾向于使用前向声明的原因是可以减少构建时间,因为它们之间具有最小的依赖性。对于没有物理依赖关系的类名,通过前向声明,编译器可以正确解析和编译程序而无需看到类声明或其所有依赖项。这样可以节省清除构建的时间,也可以节省增量构建的时间。当然,您需要花费一些更多的时间来确保每个翻译都可以看到您所需的所有标头,但这将很快得到回报,因为构建时间减少了(假设您的项目不是微不足道的)。
如果您使用
#import
或
#include
,则会向编译器抛出比必要更多的工作。还引入了复杂的头文件依赖关系。这就像暴力算法一样。当您使用
#import
时,您会带入大量不必要的信息,这需要大量的内存,磁盘I/O和CPU来解析和编译源代码。
ObjC对于基于C的语言来说非常接近理想状态,因为
NSObject
类型永远不是值--
NSObject
类型始终是引用计数指针。因此,如果适当地结构化程序的依赖关系并尽可能地前向声明,那么您可以获得非常快的编译时间,因为几乎不需要物理依赖关系。您还可以在类扩展中声明属性,以进一步最小化依赖性。对于大型系统,这是一个巨大的优势--如果您曾经开发过大型C++代码库,您会知道它所产生的差异。
因此,我的建议是尽可能使用前向声明,然后在存在物理依赖关系的情况下使用
#import
。如果看到警告或其他暗示有物理依赖关系的信息--请全部修复。解决方法是在实现文件中使用
#import
。
当构建库时,您可能会将一些接口分类为一组,在这种情况下,您应将该库
#import
引入引入物理依赖性的地方(例如
#import <AppKit/AppKit.h>
)。这可能会引入依赖关系,但库维护者通常可以根据需要处理物理依赖关系--如果他们引入一个功能,则可以将其在您的构建中最小化影响。