84

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?

0

6 Answers 6

86

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 */` 
Sign up to request clarification or add additional context in comments.

4 Comments

I started looking at typedef NS_ENUM only yesterday as a way to clean up old Objective C code - and this answer worked for me.
@lal, this has been very good for int variables. I just posted a question on how to use typedef enum for float variables. Hopefully you may be able to answer it - stackoverflow.com/q/44233973/2348597
This is also helpful if you define an @objc enum in Swift and need to use that type in a .h file. You have to forward declare it in this manner (look at your -Swift.h header to see what the raw type should be)
Sorry, I haven't looked at this question in ages. StackOverflow should really alert you if there's an answer with significantly more upvotes than the currently-accepted one. I've gone ahead and accepted this one to reflect the new best practice.
15

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).

Comments

8

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.

7 Comments

Is there any way the above is possible, without using #import? What about simply doing a typedef int EnumName?
I don't think so. See the link in gs's answer: stackoverflow.com/questions/71416/…
People recommend using @class to avoid #import cycles (where foo.h imports bar.h and bar.h imports foo.h). See the accepted answer here: stackoverflow.com/questions/9016478/…
more importantly @class protects you from circular imports.
#import is include-guard safe, for those coming from a C/C++ background.
|
4

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).

1 Comment

You can simplify this answer to this: typedef enum Enum Enum; Then just use Enum in your method definition and declaration.
2

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.

Comments

-1

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?

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.