Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different ways of declaring enum in Objective C

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;
like image 602
SilentK Avatar asked Feb 26 '18 10:02

SilentK


People also ask

Which is the correct way of declaring an enum?

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.

What is enum in Objective C?

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.

How many types of enum are there?

There are three types of enums: Numeric enum. String enum. Heterogeneous enum.


1 Answers

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:

  1. Default C enum, underlying type is int, type is used as enum WeekDays

  2. Aliased C enum, underlying type is NSUinteger, type is used as WeekDays

  3. NS_ENUM generated enum, underlying type is NSInteger, type can be referred to as either WeekDays or enum WeekDays

  4. 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!

like image 194
CRD Avatar answered Nov 17 '22 13:11

CRD