Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C Enumeration, NS_ENUM & NS_OPTIONS

What's the correct way to create an enumeration with a specific type in Objective-C? How does NS_ENUM and NS_OPTIONS work? NS_OPTIONS are used for masks, like NSAutoresizing? Thanks.

Code from NSObjCRuntime.h
    #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
    #define NS_OPTIONS(_type, _name) _type _name; enum : _type
like image 550
Lluís Avatar asked Dec 29 '12 10:12

Lluís


2 Answers

example from NSHipster. NS_OPTIONS is used in a similar way, but for enums which would ordinarily be a bit mask

instead of

typedef enum {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
} UITableViewCellStyle;

or

typedef enum {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
};

typedef NSInteger UITableViewCellStyle;

do this:

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
};

an example NS_OPTIONS enum:

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
like image 112
wattson12 Avatar answered Oct 29 '22 14:10

wattson12


There is a difference between the two except that they infer different kind of enumerations.

When compiled in Objective-C++ mode, they generate different code:

this is the original code:

typedef NS_OPTIONS(NSUInteger, MyOptionType) {
    MyOptionType1 = 1 << 0,
    MyOptionType2 = 1 << 1,
};

typedef NS_ENUM(NSUInteger, MyEnumType) {
    MyEnumType1 = 1 << 0,
    MyEnumType2 = 1 << 1,
};

this is the code when macros are expanded in Objective-C compiling:

typedef enum MyOptionType : NSUInteger MyOptionType; enum MyOptionType : NSUInteger {
    MyOptionType1 = 1 << 0,
    MyOptionType2 = 1 << 1,
};

typedef enum MyEnumType : NSUInteger MyEnumType; enum MyEnumType : NSUInteger {
    MyEnumType1 = 1 << 0,
    MyEnumType2 = 1 << 1,
};

this is the code when macros are expanded in Objective-C++ compiling:

typedef NSUInteger MyOptionType; enum : NSUInteger {
    MyOptionType1 = 1 << 0,
    MyOptionType2 = 1 << 1,
};

typedef enum MyEnumType : NSUInteger MyEnumType; enum MyEnumType : NSUInteger {
    MyEnumType1 = 1 << 0,
    MyEnumType2 = 1 << 1,
};

See the difference of NS_OPTIONS between two modes?

HERE IS THE REASON:

There is a new feature in C++ 11, you can declare a type for you enumeration, before that, the type holding enumeration is decided by compiler according to the largest value of enumerations.

So in C++ 11, since you can decide the size of your enumeration by yourself, you could forward declare enums without actually define them, like this:

//forward declare MyEnumType
enum MyEnumType: NSInteger

//use myEnumType
enum MyEnumType aVar;

//actually define MyEnumType somewhere else
enum MyEnumType: NSInteger {
    MyEnumType1 = 1 << 1,
    MyEnumType2 = 1 << 2,
}

This feature is handy, and Objective-C imports this feature , but it brings a problem, when doing bitwise calculation, like this:

enum MyEnumType aVar = MyEnumType1 | MyEnumType2;

This code can't compile in C++/Objective-C++ compiling, since aVar is considered of type NSInteger but MyEnumType1 | MyEnumType2 is of type MyEnumType, this assignment can't perform without a type cast, C++ forbids implicit type casting.

At this time, we need NS_OPTIONS, NS_OPTIONS fall back to enum before C++ 11, so that there is no MyEnumType indeed, MyEnumType is just another name for NSInteger, so that code like

enum MyEnumType aVar = MyEnumType1 | MyEnumType2; 

will compile, since it is assigning NSInteger to NSInteger.

like image 38
CarmeloS Avatar answered Oct 29 '22 14:10

CarmeloS