【问题标题】:What is the difference between NS_ENUM and NS_OPTIONS?NS_ENUM 和 NS_OPTIONS 有什么区别?
【发布时间】:2013-09-23 15:17:23
【问题描述】:

我在 Xcode5 中使用 clang 预处理了以下代码。

typedef NS_ENUM(NSInteger, MyStyle) {
    MyStyleDefault,
    MyStyleCustom
};

typedef NS_OPTIONS(NSInteger, MyOption) {
    MyOption1 = 1 << 0,
    MyOption2 = 1 << 1,
};

得到了这个。

typedef enum MyStyle : NSInteger MyStyle; enum MyStyle : NSInteger {
    MyStyleDefault,
    MyStyleCustom
};

typedef enum MyOption : NSInteger MyOption; enum MyOption : NSInteger {
    MyOption1 = 1 << 0,
    MyOption2 = 1 << 1,
};

我知道 NS_OPTIONS 用于位掩码,但有任何技术差异吗? 或者这只是为了命名约定?

编辑

根据NS_OPTIONS的定义,可能是为了编译器的兼容性。(尤其是c++编译器)

// In CFAvailability.h
// Enums and Options
#if (__cplusplus && __cplusplus >= 201103L && (__has_extension(cxx_strong_enums) || __has_feature(objc_fixed_enum))) || (!__cplusplus && __has_feature(objc_fixed_enum))
  #define CF_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
  #if (__cplusplus)
    #define CF_OPTIONS(_type, _name) _type _name; enum : _type
  #else
    #define CF_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
  #endif
#else
  #define CF_ENUM(_type, _name) _type _name; enum
  #define CF_OPTIONS(_type, _name) _type _name; enum
#endif

clang 中的 __cplusplus 值是 199711,但我无法测试它的确切用途。

【问题讨论】:

标签: ios objective-c cocoa


【解决方案1】:

枚举和位掩码(选项)之间存在基本区别。您使用枚举来列出独占状态。当多个属性可以同时应用时,使用位掩码。

在这两种情况下,您都使用整数,但您看待它们的方式不同。使用枚举查看数值,使用位掩码查看各个位。

typedef NS_ENUM(NSInteger, MyStyle) {
    MyStyleDefault,
    MyStyleCustom
};

只会代表两种状态。您可以通过测试是否相等来简单地检查它。

switch (style){
    case MyStyleDefault:
        // int is 0
    break;
    case MyStyleCustom:
        // int is 1
    break;
}

而位掩码将代表更多的状态。您可以使用逻辑或位运算符检查各个位。

typedef NS_OPTIONS(NSInteger, MyOption) {
    MyOption1 = 1 << 0, // bits: 0001
    MyOption2 = 1 << 1, // bits: 0010
};

if (option & MyOption1){ // last bit is 1
    // bits are 0001 or 0011
}
if (option & MyOption2){ // second to last bit is 1
    // bits are 0010 or 0011
}
if ((option & MyOption1) && (option & MyOption2)){ // last two bits are 1
    // bits are 0011
}

tl;dr 枚举为数字命名。位掩码为位命名。

【讨论】:

  • 天哪,你是对的!我错误地认为 1
  • 位移运算符&lt;&lt;前面的数字表示1应该向左移动多远。
  • 苹果为什么不直接写 NS_ENUM 宏来自动为你生成 1
  • 我敢说最后一个if应该是if (option &amp; (MyOption1 | MyOption2)){
  • @JoJo 你并不总是想要 (1
【解决方案2】:

唯一的主要区别是使用适当的宏可以让 Code Sense(Xcode 的代码完成)更好地进行类型检查和代码完成。例如,NS_OPTIONS 允许编译器确保您| 的所有枚举都属于同一类型。

更多阅读请见:http://nshipster.com/ns_enum-ns_options/

编辑:

现在 Swift 即将到来,强烈建议使用 NS_ENUM/OPTIONS 以便枚举可以正确桥接到 swift 枚举。

【讨论】:

  • 我认为编译器没有任何区别,尤其是在预处理之后,不再有宏,编译器会查看 OP 预处理后提供的相同代码。在参数完成方面,Xcode 不太明智。在我看来,参数完成完全基于字符串匹配。
  • 没错!许多人(包括我自己)通常将预处理器、链接器、编译器、代码完成等归为一类,称为“编译器”,这就是我在回答中的意思。我将编辑我的答案以使用更准确的术语。
  • ;) 您还可以在 Xcode 中查看声明为枚举的参数的完成情况。对于参数,Xcode 是......好吧,一无所知。 ://
【解决方案3】:

唯一的区别是让使用这些值的开发人员知道在 OR'ed 位掩码中使用它们是否有意义。

编译器并不关心你使用哪一个 :)

【讨论】:

  • “编译器并不关心你使用哪一个 :)” - 我猜你可以说编译器一点也不关心 :P
【解决方案4】:

我从这个问题Objective-C Enumeration, NS_ENUM & NS_OPTIONS复制了我的答案:

由于添加该问题的用户很长时间没有活跃,也许您可​​以为在这里搜索和找到的人建议我的答案。

以下是复制的答案

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

在 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

【讨论】:

    猜你喜欢
    • 2012-12-14
    • 2013-03-08
    • 2015-05-15
    • 2010-10-02
    • 2011-12-12
    • 2010-09-16
    • 2012-03-14
    • 2012-02-06
    相关资源
    最近更新 更多