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

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

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

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

【问题讨论】:

    标签: objective-c enums typedef extern


    【解决方案1】:

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

    【讨论】:

    • 在不使用#import 的情况下,上述方法是否可行?简单地做一个typedef int EnumName怎么样?
    • 我不这么认为。请参阅 gs 答案中的链接:stackoverflow.com/questions/71416/…
    • 人们建议使用@class 来避免#import 循环(其中 foo.h 导入 bar.h 和 bar.h 导入 foo.h)。在此处查看接受的答案:stackoverflow.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 枚举用于浮点变量的问题。希望你能回答 - stackoverflow.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。

        【讨论】:

          猜你喜欢
          • 2010-09-09
          • 2011-11-20
          • 2023-03-27
          • 1970-01-01
          • 2013-02-14
          • 1970-01-01
          相关资源
          最近更新 更多