在 Objective-C 中前向声明枚举

Posted

技术标签:

【中文标题】在 Objective-C 中前向声明枚举【英文标题】:Forward-declare enum in Objective-C 【发布时间】:2010-10-31 02:18:22 【问题描述】:

我在 Objective-C 程序中遇到枚举可见性问题。我有两个头文件,一个定义了typedef enum。另一个文件需要使用typedef'd 类型。

在直接 C 中,我会简单地 #include 另一个头文件,但在 Objective-C 中,建议不要在头文件之间使用 #import,而是根据需要使用前向 @class 声明。但是,我不知道如何前向声明枚举类型。

我不需要实际的枚举值,除非在相应的.m 实现文件中,我可以安全地#import 离开。那么如何才能在标题中识别typedef enum

【问题讨论】:

【参考方案1】:

继续使用#import。人们建议尽可能使用@class 的唯一原因是因为它使您的代码编译速度稍快。但是,#import从另一个 .h 文件中提取一个 .h 文件没有问题。事实上,你需要在扩展另一个类时这样做。

【讨论】:

在不使用#import 的情况下,上述方法是否可行?简单地做一个typedef int EnumName怎么样? 我不这么认为。请参阅 gs 答案中的链接:***.com/questions/71416/… 人们建议使用@class 来避免#import 循环(其中 foo.h 导入 bar.h 和 bar.h 导入 foo.h)。在此处查看接受的答案:***.com/questions/9016478/… 更重要的是@class 保护您免受循环导入。 #import 对于来自 C/C++ 背景的人来说是 include-guard 安全的。【参考方案2】:

无论如何,您必须要么#import 他们要么创建一个单独的头文件,只包含typedef。在头文件中不导入头文件会使编译速度更快,但不会改变任何其他内容。

Why doesn't C++ support forward declaration of enums?

【讨论】:

【参考方案3】:

你的问题的答案是要么继续导入 typedef 头文件,要么使用像 NSInteger 这样的泛型类型而不是枚举类型。

但是,不导入头文件的原因不仅仅是编译速度。

不导入头文件还可以减少对无关类的无意访问。

例如,假设您有一个 TrackFileChanges 类来跟踪文件系统对特定文件的更改,并且您有一个 CachedFile 类来存储文件中的缓存数据。后者可能使用 TrackFileChanges* 类型的私有 ivar,但对于 CachedFile 的使用,这只是一个实现细节(理想情况下,ivar 将使用新的运行时使用私有属性自动生成,但如果你'重新使用旧的运行时)。

因此,#import "CachedFile.h" 的客户端可能不需要或不想访问 TrackFileChanges.h。如果他们这样做了,他们应该自己通过#importing 来明确说明。通过在 CachedFile.h 中使用 @class TrackFileChanges instea of​​ #import "TrackFileChanges.h" 可以改进封装。

但话虽如此,如果第二个头文件想要将第一个头文件公开给所有客户端,那么从第二个头文件导入头文件并没有错。例如,声明类的头文件需要在子类化头文件中直接导入,而声明协议的头文件可能会直接导入(尽管您可以使用@protocol ABC;来避免这种情况)。

【讨论】:

【参考方案4】:

如果你可以使用编译器扩展,你可以在 Clang 中使用这个顺序:

enum Enum;
typedef enum Enum Enum2;

void f(Enum2); // ok. it sees this type's true name.

enum Enum 
    E_1
;

// ok. now its declaration is visible and we can use it.

void f(Enum2 e) 


注意:它会触发-Wpedantic 警告。


如果你使用 C++11,你应该使用它们的枚举,它们可以安全地转发声明——例如enum class Enum:uint8_t;(不是编译器扩展)。

【讨论】:

您可以将此答案简化为:typedef enum Enum Enum; 然后只需在您的方法定义和声明中使用 Enum。【参考方案5】:

在 Objective-c 中转发声明枚举 (NS_ENUM/NS_OPTION) 的最新方式(Swift 3;2017 年 5 月)是使用以下内容:

// Forward declaration for XYZCharacterType in other header say XYZCharacter.h
typedef NS_ENUM(NSUInteger, XYZCharacterType);


// Enum declaration header: "XYZEnumType.h"
#ifndef XYZCharacterType_h
#define XYZCharacterType_h

typedef NS_ENUM(NSUInteger, XYZEnumType) 
    XYZCharacterTypeNotSet,
    XYZCharacterTypeAgent,
    XYZCharacterTypeKiller,
;

#endif /* XYZCharacterType_h */`

【讨论】:

我昨天才开始将 typedef NS_ENUM 作为清理旧的 Objective C 代码的一种方法——这个答案对我有用。 @lal,这对 int 变量非常有用。我刚刚发布了一个关于如何将 typedef 枚举用于浮点变量的问题。希望你能回答 - ***.com/q/44233973/2348597 这应该是枚举前向声明的公认答案 你救了我的命。 如果您在 Swift 中定义 @objc enum 并且需要在 .h 文件中使用该类型,这也很有帮助。您必须以这种方式转发声明它(查看您的 -Swift.h 标头以查看原始类型应该是什么)【参考方案6】:

对我来说,在 Objective C .h 文件中对枚举进行前向声明的方法是查看 ProjectName-Swift.h 文件,看看它放了什么,恰好如下:

枚举 SwiftEnumName : NSInteger;

我需要这个前向声明,因为我有一个 SwiftEnumName 的函数参数类型。而且它不允许我将 ProjectName-Swift.h 导入到 Objective C .h 文件中。

然后在 Objective C .m 文件中,我只在其中添加了 #import "ProjectName-Swift.h",并且正常使用了 SwiftEnum。

这是使用 Swift 4.1.2。

【讨论】:

以上是关于在 Objective-C 中前向声明枚举的主要内容,如果未能解决你的问题,请参考以下文章

C编程中前向声明的意义是啥?

在 libc++ 的内联命名空间中前向声明类的可移植方式是啥?

在另一个函数中前向声明“constexpr”函数——编译器错误?

将声明的枚举转发为类成员变量

c++枚举类型的枚举类型的声明

Objective-C中的 id, isa,和 instancetype