Forward-declare enum in Objective-C

I'm having trouble with enum visibility in an Objective-C program. I have two header files, and one defines a typedef enum. Another file needs to use the typedef'd type.

In straight C, I would simply #include the other header file, but in Objective-C, it's recommended not to use #import between header files, instead using forward @class declarations as needed. However, I can't figure out how to forward-declare an enumeration type.

I don't need the actual enumerated values, except in the corresponding .m implementation file, where I can safely #import away. So how can I get the typedef enum to be recognized in the header?

37615 次浏览

Go ahead and use #import. The only reason people recommend to use @class when possible is because it makes your code slightly faster to compile. However, there is no issue with #importing one .h file from another. In fact, you need to do this when extending another class.

You'd have to either #import them anyway or create a separate header file containing only the typedef. Not importing header files in a header makes the compilation faster, but doesn't change anything else.

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

The answer to your question is to either go ahead and import the typedef header file or to use a generic type like NSInteger instead of the enum type.

However, there is more reason to not importing a header file than just compile speed.

Not importing a header file also reduces your inadvertent access to extraneous classes.

For example, say you have a TrackFileChanges class that tracks the file system for changes to a specific file, and you have a CachedFile class that stores cached data from a file. The latter might use a private ivar of type TrackFileChanges*, but for uses of CachedFile, this is simply an implementation detail (ideally, the ivar would be auto-generated with a private property using the new runtime, but thats not possible if you're using the old run time).

So clients that #import "CachedFile.h" probably do not need or want access to TrackFileChanges.h. And if they do, they should make it clear by #importing it themselves. By using @class TrackFileChanges instea of #import "TrackFileChanges.h" in CachedFile.h you improve the encapsulation.

But all that said, there is nothing awrong with importing a header file from a second header file if the second header wants to expose the first to all clients. For example, header files that declare classes need to be imported directly in subclassing header files, and header files declaring protocols might well be imported directly (although youy can use @protocol ABC; to avoid this).

If you are ok using compiler extensions, you could use this order in 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) {


}

Note: It will trigger a -Wpedantic warning.


If you are using C++11, you should use their enums, which are safe to forward declare -- e.g. enum class Enum:uint8_t; (not a compiler extension).

Most recent way (Swift 3; May 2017) to forward declare the enum (NS_ENUM/NS_OPTION) in objective-c is to use the following:

// 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 */`

What worked for a forward declaration of an enum for me in an Objective C .h file was look in the ProjectName-Swift.h file and see what it put, which happened to be the following:

enum SwiftEnumName : NSInteger;

I needed this forward declaration because I had a function parameter type of SwiftEnumName. And it wouldn't let me put the ProjectName-Swift.h import in the Objective C .h file.

Then in the Objective C .m file I just had the #import "ProjectName-Swift.h" in it and just used the SwiftEnum normally.

This was using Swift 4.1.2.