【问题标题】:Objective-C Enumeration, NS_ENUM & NS_OPTIONSObjective-C 枚举,NS_ENUM & NS_OPTIONS
【发布时间】:2012-12-14 09:10:33
【问题描述】:

在 Objective-C 中创建具有特定类型的枚举的正确方法是什么? NS_ENUM 和 NS_OPTIONS 是如何工作的? NS_OPTIONS 用于掩码,例如 NSAutoresizing?谢谢。

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

【问题讨论】:

  • 引用的代码是为 C++ 定义的。 C定义为:#define NS_ENUM(_type, _name) _type _name;枚举
  • 请务必在下面查看我的答案,第一个答案是正确的,但它并没有给出 NS_OPTIONS 和 NS_ENUM 之间的全部区别。

标签: objective-c cocoa enums


【解决方案1】:

来自NSHipster 的示例。 NS_OPTIONS 以类似的方式使用,但用于通常是位掩码的枚举

而不是

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

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

typedef NSInteger UITableViewCellStyle;

这样做:

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

一个示例 NS_OPTIONS 枚举:

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
};

【讨论】:

  • 但是 NS_OPTIONS 呢?
  • 就像我说的,NS_OPTIONS 的使用方式相同,但它会创建位掩码值而不是递增的枚举值
  • 即使没有 typedef NSInteger UITableViewCellStyle 也能正常工作,你为什么要添加它?
  • @mskw 你不需要添加它。该行为您提供了一些额外的编译时间检查,但这里示例的重点是展示 NS_ENUM 以更好的方式完成它
  • @mskw 重要的不是名称,而是类型。通过给一个枚举一个类型,你给了编译器关于如何处理那个枚举实例的提示。因此,如果您将其设置为 NSUInteger,它将能够在使用枚举值时执行一些基本的类型检查。
【解决方案2】:

两者之间存在区别,只是它们推断不同类型的枚举。

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

这是原始代码:

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

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

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

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++编译中扩展宏时的代码:

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_OPTIONS 的区别了吗?

HERE IS THE REASON:

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

所以在 C++ 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 引入了这个特性,但是它带来了一个问题,在进行按位计算时,像这样:

enum MyEnumType aVar = MyEnumType1 | MyEnumType2;

此代码无法在 C++/Objective-C++ 编译中编译,因为 aVar 被认为是 NSInteger 类型,但 MyEnumType1 | MyEnumType2MyEnumType 类型,如果没有类型转换,此赋值将无法执行,C++ 禁止隐式类型转换

这时候我们需要NS_OPTIONS,NS_OPTIONS回退到C++ 11之前的enum,所以确实没有MyEnumTypeMyEnumType只是NSInteger的别称,所以像这样的代码

enum MyEnumType aVar = MyEnumType1 | MyEnumType2; 

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

【讨论】:

    猜你喜欢
    • 2015-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-08
    • 2012-11-20
    相关资源
    最近更新 更多