Why are there so many different ways to declare enums in objective c? It's pretty confusing.
Is there any difference between the following or are they all the same?
enum WeekDays{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday
};
typedef enum : NSUInteger {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday
} WeekDays;
typedef NS_ENUM(NSInteger, WeekDays){
Monday,
Tuesday,
Wednesday,
Thursday,
Friday
};
enum {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday
};
typedef NSInteger WeekDays;
The keyword 'enum' is used to declare new enumeration types in C and C++. Following is an example of enum declaration. // The name of enumeration is "flag" and the constant // are the values of the flag.
An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code. If you are familiar with C, you will know that C enumerations assign related names to a set of integer values.
There are three types of enums: Numeric enum. String enum. Heterogeneous enum.
Is there any difference between the following or are they all the same?
There are differences, some due to C - which underlies Objective-C - and one if you are thinking about importing your Objective-C code into Swift.
Your first example:
enum WeekDays { Monday, ..., Friday };
is an original C enum
. This declares a type enum WeekDays
, with an underlying type of int
, and 5 literal values.
Now C is not a strongly typed language: the literal values here do not have type enum WeekDays
but rather are of type int
; and variables of type enum WeekDays
can be assigned values other than Monday
thru Friday
, and be assigned to int
variables without casts or conversions. The first literal value (Monday
here) will have the int
value 0
, and following ones will have a value one greater then the preceding literal (so here Friday
has the value 4
). Examples:
enum WeekDays one;
int two;
one = Monday;
two = Tuesday; // assigning enum literal to int is OK
two *= 42; // will produce an int value outside of Monday thru Friday
one = two; // which can be assigned to an enum WeekDays
one = 34; // int values can also be directly assigned
All the enum
variations are weak in this way. However many compilers, including those used by Xcode since at least v8, will issue warnings (not errors) in some cases, e.g. if a switch
expression is an enum
type and the case
labels either omit some of the declared literals or have values outside the declared values.
Giving the literals explicit values
Rather than relying on the default values specific values can be given to enum
literals. For example to give Monday
the value 1
through to Friday
having the value 5
:
enum WeekDays { Monday = 1, ..., Friday };
Literals without explicit values are assigned one more than the preceding literal as usual. The values do not need to be contiguous either:
enum WeekDays { Monday = 1, Tuesday = 42, Wednesday = 3, Thursday, Friday };
Using a typedef
Many C type declarations are complex and hard to parse (they are the stuff ion quiz questions), the C typedef
allows an alias for a particular type to be declared. A typedef
is often used with an enum
declaration to avoid having to use enum
when using the type. For example:
typedef enum WeekDays { Monday, ..., Friday } WeekDays;
declares the alias WeekDays
to mean enum WeekDays
. For example:
WeekDays one; // means exactly the same as the previous
// declaration for one
enum WeekDays one; // also valid and means the same thing
Seeing WeekDays
twice in the above declaration might be confusing, or simply too much typing for some people, and it can be omitted:
typedef enum { Monday, ..., Friday } WeekDays;
This is slightly different than the previous declaration, WeekDays
is now an alias for an anonymous enum
. With such a type the first form of declaration is now invalid:
enum Weekdays one; // invalid, no such enum
WeekDays one; // valid
Changing the underlying type
By default the underlying type of an enum
is int
. A different integral type can be specified, the type must be large enough to hold all the values the literals represent. The integral types are the integer, unsigned integer and character types. The underlying type is specified by adding : <type>
after the enum
tag, and the literals can be given specific values of the same type. For example:
typedef enum Vowels : char { Letter_A = 'a', ..., Letter_U = 'u' } Vowels;
as above an anonymous enum may be used:
typedef enum : char { Letter_A = 'a', ..., Letter_U = 'u' } Vowels;
Using NS_ENUM
NS_ENUM
is an (Apple) Objective-C convenience macro which expands out to a typedef
and an enum
declaration. For example:
typedef NS_ENUM(NSInteger, WeekDays) { Monday, ..., Friday };
expands to:
typedef enum WeekDays : NSInteger WeekDays;
enum WeekDays : NSInteger { Monday, ..., Friday };
which avoiding the forward declaration is equivalent to:
typedef enum WeekDays : NSInteger { Monday, ..., Friday } WeekDays;
Using Xcode v8, at least, there are no differences in compiler checks and warnings between using NS_ENUM
and the above direct typedef
.
When NS_ENUM
makes a difference
If you are writing Objective-C which will be used by Swift then using NS_ENUM
does make a difference: an enum declared using NS_ENUM
will be imported as a Swift enum
; whereas one declared directly will be imported as a Swift struct
and a collection of global read-only computed properties, one per literal.
Why this difference exists is not explained in the current Apple documentation (Using Swift with Cocoa and Objective-C (Swift 4.0.3), available through iBooks)
Finally, back to your examples
The above should enable you to figure out the differences:
Default C enum
, underlying type is int
, type is used as enum WeekDays
Aliased C enum
, underlying type is NSUinteger
, type is used as WeekDays
NS_ENUM
generated enum
, underlying type is NSInteger
, type can be referred to as either WeekDays
or enum WeekDays
This declares two distinct types and will probably produce warnings. The first is an anonymous enum
without an alias so no variables can later be introduced with this type. The literals it introduces are of type int
. The second is an alias for NSInteger
. On 64-bit systems NSInteger
is 64-bits, while int
is 32-bits, so assignments between the literal type (int
) and the "enum" type (WeekDays
) may produce warnings. Further switch
statements will not be checked as with the previous three versions, as they are simply based on NSInteger
. This version looks like an incorrect attempt to emulate NS_ENUM
and should not be used (it is invalid as such, just doesn't do what it suggests).
Hopefully all that illuminates more than it confuses!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With