关于cocoa:Objective-C Enumeration,NS_ENUM&

Objective-C Enumeration, NS_ENUM & NS_OPTIONS

在Objective-C中创建具有特定类型的枚举的正确方法是什么?ns-enum和ns-u选项如何工作?ns选项用于遮罩,如nsautoresizing?谢谢。

1
2
3
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


NShipster的示例。ns_选项以类似的方式使用,但对于通常是位掩码的枚举

而不是

1
2
3
4
5
6
typedef enum {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
} UITableViewCellStyle;

1
2
3
4
5
6
7
8
typedef enum {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
};

typedef NSInteger UITableViewCellStyle;

这样做:

1
2
3
4
5
6
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
};

示例ns_options枚举:

1
2
3
4
5
6
7
8
9
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
};


这两者之间有区别,只是他们推断出不同的枚举类型。

在Objective-C++模式下编译时,它们会生成不同的代码:

这是原始代码:

1
2
3
4
5
6
7
8
9
typedef NS_OPTIONS(NSUInteger, MyOptionType) {
    MyOptionType1 = 1 << 0,
    MyOptionType2 = 1 << 1,
};

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

这是在Objective-C编译中展开宏时的代码:

1
2
3
4
5
6
7
8
9
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,
};

这是在Objective-C++编译中展开宏时的代码:

1
2
3
4
5
6
7
8
9
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,
};

看到两种模式的ns_选项的区别了吗?

HERE IS THE REASON

在C++ 11中有一个新特性,可以为枚举声明一个类型,在此之前,根据枚举的最大值由编译器决定类型保持枚举。

因此,在C++ 11中,由于可以自行决定枚举的大小,所以可以在不实际定义它们的情况下向前声明枚举,例如:

1
2
3
4
5
6
7
8
9
10
11
//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,
}

这个特性很方便,而objective-c导入了这个特性,但是在进行逐位计算时,它带来了一个问题,如下所示:

1
enum MyEnumType aVar = MyEnumType1 | MyEnumType2;

该代码不能在C++/Objule+C++编译中编译,因为AVAR被认为是EDCOX1的类型3,而EDCOX1×4是EDCOX1×5的类型,这个分配不能在没有类型转换的情况下执行,C++禁止隐式类型的转换。

此时,我们需要NSUPTIONS,NSLY选项在C++ 11之前回落到EnUM,因此没有EDOCX1 OR 5。确实,EDCOX1 OR 5是另一个EDCOX1(3)的名称,因此代码类似

1
enum MyEnumType aVar = MyEnumType1 | MyEnumType2;

将编译,因为它将NSInteger分配给NSInteger