Что такое перечисление типов в Objective-C?

1087

Я не думаю, что я принципиально понимаю, что такое enumи когда его использовать.

Например:

typedef enum {
    kCircle,
    kRectangle,
    kOblateSpheroid
} ShapeType;

Что на самом деле здесь заявлено?

Craig
источник
2
Определяемый пользователем тип называется enum? Это то, о чем я думал, пока не наткнулся на код, имеющий несколько объявлений перечислений typedef.
Крейг,
8
Нет, пользовательский тип ShapeType. Читайте на typedef: en.wikipedia.org/wiki/Typedef
rampion
6
Определение типа в Objective-C точно такое же, как определение типа в C. И перечисление в Objective-C точно такое же, как и перечисление в C. Это объявляет перечисление с тремя константами kCircle = 0, kRectangle = 1 и kOblateSpheroid = 2 и присваивает типу enum имя ShapeType. Если вы не знаете, что означают «typedef» и «enum», купите книгу о C.
gnasher729

Ответы:

1565

Три вещи объявляются здесь: анонимный перечисляемого типа объявляется, ShapeTypeв настоящее время объявлен ЬурейеЕ для этого анонимного перечисления, и три имени kCircle, kRectangleи kOblateSpheroidобъявляются в качестве интегральных констант.

Давайте разберемся с этим. В простейшем случае перечисление может быть объявлено как

enum tagname { ... };

Это объявляет перечисление с тегом tagname. В C и Objective-C (но не в C ++) любые ссылки на это должны предшествовать enumключевым словом. Например:

enum tagname x;  // declare x of type 'enum tagname'
tagname x;  // ERROR in C/Objective-C, OK in C++

Чтобы избежать необходимости enumвезде использовать ключевое слово, можно создать typedef:

enum tagname { ... };
typedef enum tagname tagname;  // declare 'tagname' as a typedef for 'enum tagname'

Это можно упростить в одну строку:

typedef enum tagname { ... } tagname;  // declare both 'enum tagname' and 'tagname'

И , наконец, если мы не должны быть в состоянии использовать enum tagnameс enumключевым словом, мы можем сделать enumанонимное и только объявить его с именем ЬурейиМ:

typedef enum { ... } tagname;

Теперь, в этом случае, мы объявляем, ShapeTypeчто это имя типа анонимного перечисления, определенное с помощью typedef. ShapeTypeна самом деле просто целочисленный тип, и должен использоваться только для объявления переменных , которые удерживают одно из значений , указанных в декларации (то есть, один из kCircle, kRectangle, и kOblateSpheroid). Вы можете присвоить ShapeTypeпеременной другое значение путем приведения, поэтому вы должны быть осторожны при чтении значений перечисления.

И, наконец, kCircle, kRectangleи kOblateSpheroidобъявлены как интегральные константы в глобальном пространстве имен. Поскольку конкретные значения не указаны, они присваиваются последовательным целым числам, начиная с 0, поэтому kCircle0, kRectangle1, kOblateSpheroid2.

Адам Розенфилд
источник
6
Хорошее объяснение - просто чтобы добавить одну вещь, структура придерживается аналогичных правил именования в C (не уверен насчет Objective-C).
Майкл Берр
109
Objective-C является правильным надмножеством C. Все правила именования структур C в C так же действительны в Objective-C.
sigjuice
Потрясающие. Могу ли я просто использовать enum в стиле C ++, а также не нужно писать enum :)
user4951
11
Вы можете использовать перечисления в стиле C ++, если файл, в котором вы их объявляете, является .mm-файлом, а не .m. Objective-C ++ невероятно мощный.
Кевин Хоффман
14
И как только вы обдумаете этот ответ, стоит взглянуть на новые NS_ENUM и NS_OPTIONS. Учебное пособие здесь: nshipster.com/ns_enum-ns_options и ТАК здесь: stackoverflow.com/questions/14080750/…
Snowcrash
254

Apple рекомендует определять перечисления как это начиная с Xcode 4.4 :

typedef enum ShapeType : NSUInteger {
    kCircle,
    kRectangle,
    kOblateSpheroid
} ShapeType;

Они также предоставляют удобный макрос NS_ENUM:

typedef NS_ENUM(NSUInteger, ShapeType) {
    kCircle,
    kRectangle,
    kOblateSpheroid
};

Эти определения обеспечивают более строгую проверку типов и лучшее завершение кода. Я не смог найти официальную документацию NS_ENUM, но вы можете посмотреть видео "Modern Objective-C" с сессии WWDC 2012 здесь .


ОБНОВЛЕНИЕ
Ссылка на официальную документацию здесь .

Владимир Григоров
источник
13
Часть о «Enum Improvements» начинается в 5:58
vikingosegundo
5
Как прокомментировал другой ответ, см. Объяснение NS_ENUMмакроса Apple от NSHipster: NSHipster.com/ns_enum-ns_options
Василий Бурк
1
Это ссылка на официальную документацию о NS_ENUM: developer.apple.com/library/ios/releasenotes/ObjectiveC/…
YoGiN
50

Перечисление объявляет набор упорядоченных значений - typedef просто добавляет к этому удобное имя. 1-й элемент 0 и т. Д.

typedef enum {
Monday=1,
...
} WORKDAYS;

WORKDAYS today = Monday;

Выше приведено только перечисление тегов shapeType.

hburde
источник
34

Пользователь определен тип , который имеет возможные значения kCircle, kRectangleили kOblateSpheroid. Однако значения внутри перечисления (kCircle и т. Д.) Видны за пределами перечисления. Важно помнить это ( int i = kCircle;допустимо, например).

Брайан Митчелл
источник
30

Обновление для 64-битных изменений: согласно документам Apple о 64-битных изменениях,

Перечисления также типизированы: в компиляторе LLVM перечислимые типы могут определять размер перечисления. Это означает, что некоторые перечисляемые типы могут также иметь размер, который больше, чем вы ожидаете. Решение, как и во всех других случаях, состоит в том, чтобы не делать предположений о размере типа данных. Вместо этого присвойте любые перечисляемые значения переменной с правильным типом данных.

Поэтому вы должны создать enum с типом, как показано ниже, с синтаксисом, если вы поддерживаете 64-битную версию.

typedef NS_ENUM(NSUInteger, ShapeType) {
    kCircle,
    kRectangle,
    kOblateSpheroid
};

или

typedef enum ShapeType : NSUInteger {
   kCircle,
   kRectangle,
   kOblateSpheroid
} ShapeType;

В противном случае это приведет к предупреждению Implicit conversion loses integer precision: NSUInteger (aka 'unsigned long') to ShapeType

Обновление для swift-программирования:

В быстром, есть изменение синтаксиса.

enum ControlButtonID: NSUInteger {
        case kCircle , kRectangle, kOblateSpheroid
    }
Mani
источник
Если потребность в прямое возвещайте перечисление (NS_ENUM): stackoverflow.com/a/42009056/342794
лал
25

Перечисление (сокращение от перечисления) используется для перечисления набора значений (перечислителей). Значение - это абстрактная вещь, представленная символом (словом). Например, базовое перечисление может быть

enum { xs,s,m,l,xl,xxl,xxxl,xxxxl };

Это перечисление называется анонимным, потому что у вас нет символа, чтобы назвать его. Но это все еще совершенно правильно. Просто используйте это так

enum { xs,s,m,l,xl,xxl,xxxl,xxxxl } myGrandMotherDressSize;

Хорошо. Жизнь прекрасна, и все идет хорошо. Но однажды вам нужно повторно использовать это перечисление, чтобы определить новую переменную для хранения myGrandFatherPantSize, и тогда вы напишите:

enum { xs,s,m,l,xl,xxl,xxxl,xxxxl } myGrandMotherDressSize;
enum { xs,s,m,l,xl,xxl,xxxl,xxxxl } myGrandFatherPantSize;

Но тогда у вас есть ошибка компилятора "переопределение перечислителя". На самом деле, проблема в том, что компилятор не уверен, что вы сначала перечислили, а вы - второе, описали то же самое.

Затем, если вы хотите повторно использовать один и тот же набор перечислителей (здесь xs ... xxxxl) в нескольких местах, вы должны пометить его уникальным именем. Во второй раз, когда вы используете этот набор, вы просто должны использовать тег. Но не забывайте, что этот тег не заменяет слово enum, а только набор перечислителей. Затем позаботьтесь о том, чтобы использовать enum как обычно. Нравится:

// Here the first use of my enum
enum sizes { xs,s,m,l,xl,xxl,xxxl,xxxxl } myGrandMotherDressSize; 
// here the second use of my enum. It works now!
enum sizes myGrandFatherPantSize;

Вы также можете использовать его в определении параметра:

// Observe that here, I still use the enum
- (void) buyANewDressToMyGrandMother:(enum sizes)theSize;

Можно сказать, что переписывать enum везде не удобно, и код выглядит немного странно. Вы правы. Реальный тип был бы лучше.

Это последний шаг нашего великого продвижения к вершине. Просто добавив typedef, давайте превратим наше перечисление в реальный тип. О, последнее, typedef не разрешен в вашем классе. Затем определите ваш тип выше. Делай это так:

// enum definition
enum sizes { xs,s,m,l,xl,xxl,xxxl,xxxxl };
typedef enum sizes size_type

@interface myClass {
   ...
   size_type myGrandMotherDressSize, myGrandFatherPantSize;
   ...
}

Помните, что тег необязателен. Тогда, поскольку здесь, в этом случае, мы не помечаем перечислители, а просто определяем новый тип. Тогда нам это больше не нужно.

// enum definition
typedef enum { xs,s,m,l,xl,xxl,xxxl,xxxxl } size_type;

@interface myClass : NSObject {
  ...
  size_type myGrandMotherDressSize, myGrandFatherPantSize;
  ...
}
@end

Если вы разрабатываете в Objective-C с XCode, я позволю вам обнаружить несколько хороших макросов с префиксом NS_ENUM. Это должно помочь вам легко определить хорошие перечисления и, кроме того, поможет статическому анализатору выполнить некоторые интересные проверки перед сборкой.

Хороший Enum!

Винсент Згеб
источник
Я всегда думал, «зачем кому-то отвечать на вопрос, на который уже дан ответ и принят». Мальчик, я все время ошибался! Это лучший ответ и помогает начинающим, как я!
rak appdev
10

typedefполезно для переопределения имени существующего типа переменной. Это обеспечивает короткий и значимый способ вызова типа данных. например:

typedef unsigned long int TWOWORDS;

здесь тип unsigned long int переопределяется как тип TWOWORDS. Таким образом, теперь мы можем объявить переменные типа unsigned long int, написав

TWOWORDS var1, var2;

вместо

unsigned long int var1, var2;
Rajneesh071
источник
7
typedef enum {
kCircle,
kRectangle,
kOblateSpheroid
} ShapeType;

тогда вы можете использовать его как: -

 ShapeType shape;

а также

 enum {
    kCircle,
    kRectangle,
    kOblateSpheroid
} 
ShapeType;

Теперь вы можете использовать его как: -

enum ShapeType shape;
Вивек Сехрават
источник
3

enum используется для присвоения значения элементам enum, что невозможно сделать в структуре. Таким образом, каждый раз вместо доступа к полной переменной мы можем делать это по значению, которое мы присваиваем переменным в enum. По умолчанию он начинается с присвоения 0, но мы можем присвоить ему любое значение, а следующей переменной в enum будет присвоено значение, равное предыдущему значению +1.

Приянка Найк
источник
3

Вы можете использовать в следующем формате, необработанное значение по умолчанию, начиная с 0, так

  • kCircle равно 0,
  • kRectangle равен 1,
  • kOblateSpheroid - 2.

Вы можете назначить свое собственное начальное значение.

typedef enum : NSUInteger {
    kCircle, // for your value; kCircle = 5, ...
    kRectangle,
    kOblateSpheroid
} ShapeType;

ShapeType circleShape = kCircle;
NSLog(@"%lu", (unsigned long) circleShape); // prints: 0
Билал Арслан
источник
2

Определение типа позволяет программисту определять один тип Objective C как другой. Например,

typedef int Counter; определяет тип Counter как эквивалентный типу int. Это значительно улучшает читабельность кода.


источник
2

Typedef - ключевое слово в C и C ++. Он используется для создания новых имен для основных типов данных (char, int, float, double, struct & enum) .

typedef enum {
    kCircle,
    kRectangle,
    kOblateSpheroid
} ShapeType;

Здесь он создает перечислимый тип данных ShapeType, и мы можем написать новые имена для перечисляемого типа ShapeType, как показано ниже

ShapeType shape1; 
ShapeType shape2; 
ShapeType shape3;
Йогиш ХТ
источник
1

enum может уменьшить количество типов «ошибок» и сделать код более управляемым

#define STATE_GOOD 0
#define STATE_BAD 1
#define STATE_OTHER 2
int STATE = STATE_OTHER

Определение не имеет ограничений. Это просто замена. Он не способен ограничивать все условия государства. Когда STATE назначен на 5, программа будет неправильной, потому что нет соответствующего состояния. Но компилятор не собирается предупреждать STATE = 5

Так что лучше так использовать

typedef enum SampleState {
    SampleStateGood  = 0,
    SampleStateBad,
    SampleStateOther
} SampleState;

SampleState state = SampleStateGood;
Маркус Торнтон
источник