82

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?

Stephen Touset
  • 2,562
  • 2
  • 25
  • 24

6 Answers6

83

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 */`
lal
  • 7,410
  • 7
  • 34
  • 45
  • 1
    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. – Greg May 28 '17 at 05:44
  • @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 - https://stackoverflow.com/q/44233973/2348597 – Greg May 29 '17 at 11:50
  • 2
    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) – Max Feb 04 '19 at 21:25
  • 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. – Stephen Touset Nov 21 '21 at 23:17
16

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

Peter N Lewis
  • 17,664
  • 2
  • 43
  • 56
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.

Sebastian Celis
  • 12,185
  • 6
  • 36
  • 44
  • 1
    Is there any way the above is possible, without using #import? What about simply doing a `typedef int EnumName`? – Stephen Touset Jun 03 '09 at 19:01
  • 1
    I don't think so. See the link in gs's answer: http://stackoverflow.com/questions/71416/forward-declaring-an-enum-in-c – Sebastian Celis Jun 03 '09 at 19:09
  • With #import compilation needs minutes, and there is some wired thing anyway. – János Jun 26 '11 at 19:00
  • 39
    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: http://stackoverflow.com/questions/9016478/why-class-instead-of-import-for-viewcontroller-in-appdelegate-h – alexkent Feb 14 '14 at 13:12
  • 6
    more importantly @class protects you from circular imports. – Ozgur Vatansever Jun 26 '14 at 15:05
  • 3
    #import is include-guard safe, for those coming from a C/C++ background. – MattD Mar 11 '15 at 17:46
  • 1
    The explicit request is not to use #import. There are other reasons not ot use it other than compilation time. It also is definitely possible if you see lal's answer. – Alper Mar 16 '18 at 07:58
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).

justin
  • 104,054
  • 14
  • 179
  • 226
  • 1
    You can simplify this answer to this: `typedef enum Enum Enum;` Then just use Enum in your method definition and declaration. – Sandy Chapman Mar 25 '14 at 14:01
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.

Doug Voss
  • 1,012
  • 1
  • 14
  • 11
-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?

Community
  • 1
  • 1
Georg Schölly
  • 124,188
  • 49
  • 220
  • 267