Що таке перерахунок typedef в Objective-C?


1087

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

Наприклад:

typedef enum {
    kCircle,
    kRectangle,
    kOblateSpheroid
} ShapeType;

Що насправді декларується тут?


2
Чи визначений користувачем тип "enum"? Ось що я і думав, поки не натрапив на код, який мав кілька декларацій переліку endef.
Крейг

8
Ні, тип, визначений користувачем, є ShapeType. Читайте на typedef: en.wikipedia.org/wiki/Typedef
чемпіон

6
Typedef в Objective-C точно такий же, як typedef в C. І перерахунок у Objective-C точно такий же, як enum у C. Це оголошує enum з трьома константами 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напевне ім'я анонімного перерахування. ShapeTypeнасправді просто цілочисельний тип, і повинен використовуватися тільки для оголошення змінних , які утримують одне зі значень , зазначених в декларації (тобто, один з kCircle, kRectangle, і kOblateSpheroid). ShapeTypeОднак ви можете призначити змінній інше значення, використовуючи кастинг, тому вам слід бути обережними, читаючи значення перерахунків.

І, нарешті, kCircle, kRectangleі kOblateSpheroidоголошені як інтегральні константи в глобальному просторі імен. Оскільки конкретних значень не було визначено, вони присвоюються послідовним цілим числам, починаючи з 0, так kCircleце 0, kRectangleдорівнює 1 і kOblateSpheroidдорівнює 2.


6
Приємне пояснення - просто додати одне, структура дотримується подібних правил іменування в C (не впевнені в Objective-C).
Майкл Берр

109
Objective-C є належним набором C. Всі правила іменування C структури в C так само справедливі і в Objective-C.
sigjuice

Дивовижно. Чи можу я просто використовувати перелік стилю C ++, а також не потрібно писати enum :)
user4951

11
Ви можете використовувати переліки стилю C ++, якщо файл, у якому ви їх заявляєте, - це .mm-файл, а не .m. Objective-C ++ є абсурдно потужним.
Кевін Хоффман

14
І як тільки ви поцікавитеся цією відповіддю, варто поглянути на нові NS_ENUM та NS_OPTIONS. Підручник тут: nshipster.com/ns_enum-ns_options і SO тут: 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, але ви можете переглянути відео "Сучасний об'єктив-C" з сесії WWDC 2012 тут .


ОНОВЛЕННЯ
Посилання на офіційну документацію тут .


13
Частина про "Поліпшення Енума" починається о 5:58
вікіосегундо

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.


34

Користувач визначено тип , який має можливі значення kCircle, kRectangleчи kOblateSpheroid. Значення всередині enum (kCircle тощо), однак, видно і поза enum. Важливо пам’ятати про це ( 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

Оновлення для швидкого програмування:

Швидко відбувається зміна синтаксису.

enum ControlButtonID: NSUInteger {
        case kCircle , kRectangle, kOblateSpheroid
    }

Якщо потреба в пряме співом перерахування (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, а лише набір нумераторів. Тоді подбайте про використання енту як завжди. Подобається це:

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

Можна сказати, що переписування переписок скрізь не є зручним, і код виглядає дещо дивно. Ти правий. Реальний тип був би кращим.

Це завершальний крок нашого великого прогресу до саміту. Просто додавши 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. Це повинно допомогти вам легко визначити хороші перерахунки, і, крім того, допоможе статичному аналізатору зробити кілька цікавих перевірок перед тим, як компілювати.

Добрий Енум!


Я завжди думав, "чому хтось відповість на питання, на яке вже відповіли та прийняли". Хлопчик, я весь час помилявся! Це найкраща відповідь і допомагає новачкам, як я!
rak appdev

10

typedefє корисним для перевизначення імені існуючого типу змінної. Це забезпечує короткий та змістовний спосіб викликати тип даних. наприклад:

typedef unsigned long int TWOWORDS;

тут тип неподписаного довгого int переосмислюється як тип TWOWORDS. Таким чином, тепер ми можемо оголошувати змінні типу без підпису long int, записуючи,

TWOWORDS var1, var2;

замість

unsigned long int var1, var2;

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

Typedef дозволяє програмісту визначити один тип Objective-C як інший. Наприклад,

typedef int Counter; визначає тип Counter, який є еквівалентним типу int. Це різко покращує читабельність коду.


2

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

typedef enum {
    kCircle,
    kRectangle,
    kOblateSpheroid
} ShapeType;

Тут створюються перелічені типи даних ShapeType, і ми можемо записати нові імена для enum типу 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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.