Подтвердить что ты не робот

В чем разница между NS_ENUM и NS_OPTIONS?

Я предварительно обработал следующий код с помощью clang в Xcode5.

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 это возможно для совместимости компилятора (особенно для компилятора С++)

// 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

Значение __ cplusplus в clang равно 199711, и я не могу проверить, что это именно так.

4b9b3361

Ответ 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 Перечисление дает имена для чисел. Битовая маска дает имена битам.

Ответ 2

Единственное существенное отличие состоит в том, что использование соответствующего макроса позволяет Code Sense (завершение кода Xcode) лучше выполнять проверку типов и завершение кода. Например, NS_OPTIONS позволяет компилятору убедиться, что все перечисления, которые вы | вместе, имеют один и тот же тип.

Для дальнейшего чтения см. http://nshipster.com/ns_enum-ns_options/

Edit:

Теперь, когда Swift подходит, настоятельно рекомендуется использовать NS_ENUM/OPTIONS, чтобы перечислимое перечисление могло быть правильно соединено с быстрым перечислением.

Ответ 3

Единственное отличие состоит в том, чтобы позволить разработчикам, использующим значения, знать, имеет ли смысл использовать их в макете OR 'ed.

Компилятору все равно, какой из них вы используете:)

Ответ 4

Я скопировал свой ответ из этого вопроса Objective-C Перечисление, 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:

В С++ 11 есть новая функция, вы можете объявить тип для перечисления, до этого перечисление типа hold определяется компилятором в соответствии с наибольшим значением перечислений.

Итак, в С++ 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;

Этот код не может компилироваться в компиляции С++/ Objective-C ++, так как aVar рассматривается как тип NSInteger, но MyEnumType1 | MyEnumType2 имеет тип MyEnumType, это назначение не может выполняться без литья типов, С++ запрещает неявное литье типов.

В настоящее время нам нужны NS_OPTIONS, NS_OPTIONS возвращаются к перечислению перед С++ 11, так что нет MyEnumType действительно, MyEnumType - это просто другое имя для NSInteger, поэтому код типа

enum MyEnumType aVar = MyEnumType1 | MyEnumType2; 

будет компилироваться, поскольку он присваивает NSInteger NSInteger.