Перечисление с прямым объявлением в Objective-C

У меня проблемы с видимостью enum в программе Objective-C. У меня есть два файла заголовков, один из которых определяет typedef enum. Другой файл должен использовать тип typedef'd.

В прямом C я бы просто #include другой файл заголовка, но в Objective-C рекомендуется не использовать #import между файлами заголовков, вместо этого при необходимости используйте @class объявления вперед. Однако я не могу понять, как переадресовать объявление типа перечисления.

Мне не нужны фактические перечисленные значения, за исключением соответствующего .m файла реализации, где я могу безопасно #import уйти. Итак, как мне заставить typedef enum распознаваться в шапке?


person Stephen Touset    schedule 03.06.2009    source источник


Ответы (6)


Продолжайте и используйте #import. Единственная причина, по которой люди рекомендуют использовать @class, когда это возможно, заключается в том, что это немного ускоряет компиляцию вашего кода. Однако нет проблем с #import копированием одного файла .h из другого. Фактически, вам нужно сделать это при расширении другого класса.

person Sebastian Celis    schedule 03.06.2009
comment
Возможно ли это каким-либо образом без использования #import? А как насчет того, чтобы просто сделать typedef int EnumName? - person Stephen Touset; 03.06.2009
comment
Я так не думаю. См. Ссылку в ответе gs: stackoverflow.com / questions / 71416 / - person Sebastian Celis; 03.06.2009
comment
С #import компиляция требует минут, и в любом случае есть какие-то проводные штуки. - person János; 26.06.2011
comment
Люди рекомендуют использовать @class, чтобы избежать циклов #import (где foo.h импортирует bar.h, а bar.h импортирует foo.h). См. Принятый ответ здесь: stackoverflow.com/questions/9016478/ - person alexkent; 14.02.2014
comment
что еще более важно, @class защищает вас от циклического импорта. - person ozgur; 26.06.2014
comment
#import безопасен для include-guard для тех, кто работает с C / C ++. - person MattD; 11.03.2015
comment
Явный запрос не должен использовать #import. Есть и другие причины не использовать его, кроме времени компиляции. Это тоже определенно возможно, если вы увидите ответ Лала. - person Alper; 16.03.2018

Самый последний способ (Swift 3; май 2017 г.) пересылки объявления перечисления (NS_ENUM / NS_OPTION) в объекте-c заключается в использовании следующего:

// Forward declaration for XYZCharacterType in other header say XYZCharacter.h
typedef NS_ENUM(NSUInteger, XYZCharacterType);


// Enum declaration header: "XYZEnumType.h"
#ifndef XYZCharacterType_h
#define XYZCharacterType_h

typedef NS_ENUM(NSUInteger, XYZEnumType) {
    XYZCharacterTypeNotSet,
    XYZCharacterTypeAgent,
    XYZCharacterTypeKiller,
};

#endif /* XYZCharacterType_h */`
person lal    schedule 02.02.2017
comment
Я только вчера начал смотреть на typedef NS_ENUM как на способ очистить старый код Objective C - и этот ответ сработал для меня. - person Greg; 28.05.2017
comment
@lal, это было очень хорошо для переменных типа int. Я только что разместил вопрос о том, как использовать перечисление typedef для переменных с плавающей запятой. Надеюсь, вы сможете ответить на него - stackoverflow.com/q/44233973/2348597 - person Greg; 29.05.2017
comment
Это должен быть принятый ответ для форвардного объявления Enum - person RubenVot; 08.03.2018
comment
Вы спасли мою жизнь. - person DawnSong; 08.01.2019
comment
Это также полезно, если вы определяете @objc enum в Swift и вам нужно использовать этот тип в .h файле. Вы должны переслать объявление таким образом (посмотрите на заголовок -Swift.h, чтобы узнать, каким должен быть исходный тип) - person Max; 05.02.2019

Ответ на ваш вопрос - либо продолжить и импортировать файл заголовка typedef, либо использовать общий тип, такой как NSInteger, вместо типа enum.

Однако есть больше причин не импортировать файл заголовка, чем просто скорость компиляции.

Отказ от импорта файла заголовка также снижает вероятность непреднамеренного доступа к посторонним классам.

Например, предположим, что у вас есть класс TrackFileChanges, который отслеживает изменения файловой системы в конкретном файле, и класс CachedFile, в котором хранятся кэшированные данные из файла. Последний может использовать частный ivar типа TrackFileChanges *, но для использования CachedFile это просто деталь реализации (в идеале ivar будет автоматически сгенерирован с частным свойством с использованием новой среды выполнения, но это невозможно, если вы ' повторно используем старую среду выполнения).

Таким образом, клиентам, которые #import "CachedFile.h", вероятно, не нужен или не нужен доступ к TrackFileChanges.h. И если они это сделают, они должны прояснить это, # импортировав это сами. Используя @class TrackFileChanges вместо #import "TrackFileChanges.h" в CachedFile.h, вы улучшаете инкапсуляцию.

Но все, что было сказано, нет ничего плохого в импорте файла заголовка из второго файла заголовка, если второй заголовок хочет открыть первый для всех клиентов. Например, файлы заголовков, которые объявляют классы, необходимо импортировать непосредственно в файлы заголовков подклассов, а файлы заголовков, объявляющие протоколы, вполне могут быть импортированы напрямую (хотя вы можете использовать @protocol ABC; чтобы этого избежать).

person Peter N Lewis    schedule 04.06.2009

Если у вас все в порядке с расширениями компилятора, вы можете использовать этот порядок в Clang:

enum Enum;
typedef enum Enum Enum2;

void f(Enum2); // ok. it sees this type's true name.

enum Enum {
    E_1
};

// ok. now its declaration is visible and we can use it.

void f(Enum2 e) {

}

Примечание: вызовет -Wpedantic предупреждение.


Если вы используете C ++ 11, вам следует использовать их перечисления, которые можно безопасно пересылать объявлениям - например, enum class Enum:uint8_t; (не расширение компилятора).

person justin    schedule 08.08.2013
comment
Вы можете упростить этот ответ до следующего: typedef enum Enum Enum; Затем просто используйте Enum в определении и объявлении вашего метода. - person Sandy Chapman; 25.03.2014

Что сработало для прямого объявления перечисления для меня в файле Objective C .h, так это посмотреть в файле ProjectName-Swift.h и посмотреть, что он поместил, что оказалось следующим:

перечисление SwiftEnumName: NSInteger;

Мне было нужно это предварительное объявление, потому что у меня был тип параметра функции SwiftEnumName. И это не позволило мне поместить импорт ProjectName-Swift.h в файл Objective C .h.

Затем в файле Objective C .m у меня был только #import "ProjectName-Swift.h", и я просто обычно использовал SwiftEnum.

Это было с использованием Swift 4.1.2.

person Doug Voss    schedule 17.07.2018

В любом случае вам придется либо #import их, либо создать отдельный файл заголовка, содержащий только typedef. Отсутствие импорта файлов заголовков в заголовок ускоряет компиляцию, но ничего не меняет.

Почему C ++ не поддерживает прямое объявление перечислений?

person Georg Schölly    schedule 03.06.2009