#ifdef замена в языке Swift

512

В C/С++/Objective-C вы можете определить макрос, используя препроцессоры компилятора. Более того, вы можете включать/исключать некоторые части кода с помощью препроцессоров компилятора.

#ifdef DEBUG
    // Debug-only code
#endif

Есть ли аналогичное решение в Swift?

  • 1
    Как идея, вы могли бы поместить это в ваши мостовые заголовки obj-c ..
  • 21
    Вы действительно должны присудить ответ, поскольку у вас есть несколько вариантов на выбор, и этот вопрос принес вам много голосов.
Теги:
xcode
preprocessor
preprocessor-directive

9 ответов

784

Да, вы можете это сделать.

В Swift вы все равно можете использовать макросы препроцессора "# if/# else/# endif" (хотя и более ограниченные), согласно Apple docs. Вот пример:

#if DEBUG
    let a = 2
#else
    let a = 3
#endif

Теперь вы должны установить символ "DEBUG" в другом месте. Установите его в разделе "Swift Compiler - Custom Flags", "Other Swift Flags". Вы добавляете символ DEBUG с записью -D DEBUG.

Как обычно, вы можете установить другое значение в Debug или в Release.

Я тестировал его в реальном коде, и он работает; он, по-видимому, не распознается на игровой площадке.

Вы можете прочитать мой оригинальный пост here.


ВАЖНОЕ ПРИМЕЧАНИЕ: -DDEBUG=1 не работает. Работает только -D DEBUG. Кажется, компилятор игнорирует флаг с определенным значением.

  • 38
    Это правильный ответ, хотя следует отметить, что вы можете проверять только наличие флага, но не конкретное значение.
  • 0
    Ну, это неприятно. Я часто использую #if 0 / # else / # endif, чтобы попробовать тестовый код. Это делает тривиальным, чтобы идти вперед и назад, чтобы проверить такие вещи, как долговечность или скорость. Я понимаю, что препроцессор был просто еще одним взломом (взломать взломать). Но в этом случае это действительно была полезная вещь
Показать ещё 25 комментариев
228

Как указано в Apple Docs

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

Мне удалось добиться того, что я хотел, используя пользовательские конфигурации сборки:

  • Перейдите в свой проект/выберите цель/Настройки сборки/поиск пользовательских флагов
  • Для выбранной вами цели установите свой собственный флаг с использованием префикса -D (без пробелов), как для отладки, так и для выпуска
  • Выполняйте шаги выше для каждой вашей цели.

Здесь вы проверяете цель:

#if BANANA
    print("We have a banana")
#elseif MELONA
    print("Melona")
#else
    print("Kiwi")
#endif

Изображение 1591

Протестировано с использованием Swift 2.2

  • 4
    1. с пробелами также работает, 2. должен установить флаг только для отладки?
  • 3
    @ c0ming зависит от ваших потребностей, но если вы хотите, чтобы что-то происходило только в режиме отладки, а не в выпуске, вам нужно удалить -DDEBUG из Release.
Показать ещё 6 комментариев
138

Во многих ситуациях вам не нужна условная компиляция; вам просто нужно условное поведение, которое вы можете включить и выключить. Для этого вы можете использовать переменную окружения. Это имеет огромное преимущество, которое вам не нужно перекомпилировать.

Вы можете установить переменную окружения и легко включить или отключить ее в редакторе схем:

Изображение 1592

Вы можете получить переменную окружения с помощью NSProcessInfo:

    let dic = NSProcessInfo.processInfo().environment
    if dic["TRIPLE"] != nil {
        // ... do secret stuff here ...
    }

Вот пример реальной жизни. Мое приложение работает только на устройстве, потому что оно использует музыкальную библиотеку, которая не существует на Симуляторе. Как же тогда снимать скриншоты на Симуляторе для устройств, которых у меня нет? Без этих снимков экрана я не могу отправить AppStore.

Мне нужны поддельные данные и другой способ его обработки. У меня есть две переменные среды: одна, которая при включении сообщает приложению, чтобы генерировать поддельные данные из реальных данных во время работы на моем устройстве; другой, который при включении использует поддельные данные (а не отсутствующую музыкальную библиотеку) во время работы на симуляторе. Переключение каждого из этих специальных режимов вкл/выкл легко благодаря флагом переменной окружения в редакторе Схемы. И бонус заключается в том, что я не могу случайно использовать их в своей сборке App Store, потому что в архиве нет переменных среды.

  • 0
    По какой-то причине моя переменная среды вернулась как ноль при втором запуске приложения
  • 52
    Осторожно : переменные среды устанавливаются для всех конфигураций сборки, их нельзя устанавливать для отдельных. Так что это не жизнеспособное решение, если вам нужно изменить поведение в зависимости от того, является ли это выпуском или отладочной сборкой.
Показать ещё 11 комментариев
78

Основное изменение замены ifdef вызвало Xcode 8. Использование Активных условий компиляции.

Обратитесь к Создание и привязка в Xcode 8 Release note.

Новые настройки сборки

Новая настройка: SWIFT_ACTIVE_COMPILATION_CONDITIONS

"Active Compilation Conditions" is a new build setting for passing conditional compilation flags to the Swift compiler.

Раньше нам приходилось объявлять ваши условные флаговые компиляции в разделе OTHER_SWIFT_FLAGS, не забывая добавлять в параметр параметр "-D". Например, чтобы условно скомпилировать значение MYFLAG:

#if MYFLAG1
    // stuff 1
#elseif MYFLAG2
    // stuff 2
#else
    // stuff 3
#endif

Значение для добавления к настройке -DMYFLAG

Теперь нам нужно передать значение MYFLAG в новый параметр. Время для перемещения всех этих условных значений компиляции!

Обратитесь к ссылке ниже для более быстрой настройки параметров настройки в Xcode 8: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-analyzer-improvements/

  • 0
    Есть ли способ отключить установленные условия активной компиляции во время сборки? Мне нужно отключить условие DEBUG при создании конфигурации отладки для тестирования.
  • 1
    @Jonny Единственный способ, который я нашел, - создать третью конфигурацию сборки для проекта. На вкладке «Проект»> «Информация»> «Конфигурации» нажмите «+», затем дублируйте «Отладка». Затем вы можете настроить условия активной компиляции для этой конфигурации. Не забудьте отредактировать свою схему Target> Test, чтобы использовать новую конфигурацию сборки!
Показать ещё 3 комментария
66

Нет препроцессора Swift. (Во-первых, произвольная замена кода прерывает безопасность типов и памяти.)

Swift также включает параметры конфигурации времени сборки, поэтому вы можете условно включать код для определенных платформ или стилей сборки или в ответ на флаги, которые вы определяете с помощью -D compiler args. В отличие от C, условно скомпилированный раздел вашего кода должен быть синтаксически завершен. Вот раздел об этом в Использование Swift с Cocoa и Objective-C.

Например:

#if os(iOS)
    let color = UIColor.redColor()
#else
    let color = NSColor.redColor()
#endif
  • 33
    «С одной стороны, произвольная замена кода нарушает безопасность типов и памяти». Разве препроцессор не выполняет свою работу раньше, чем это делает компилятор (отсюда и название)? Таким образом, все эти проверки все еще могут иметь место.
  • 10
    @ Thilo Я думаю, что это ломает поддержку IDE
Показать ещё 5 комментариев
63

Как и в случае Swift 3.1, если вам нужно всего лишь проверить, построен ли код с настройкой отладки или выпуска, вы можете использовать встроенные функции:

  • _isDebugAssertConfiguration() (true, когда оптимизация установлена ​​на -Onone)
  • _isReleaseAssertConfiguration() (true, когда оптимизация установлена ​​на -O) (недоступно в Swift 3 +)
  • _isFastAssertConfiguration() (true, когда оптимизация установлена ​​на -Ounchecked)

например.

func obtain() -> AbstractThing {
    if _isDebugAssertConfiguration() {
        return DecoratedThingWithDebugInformation(Thing())
    } else {
        return Thing()
    }
}

По сравнению с макросами препроцессора,

  • ✓ Вам не нужно определять пользовательский флаг -D DEBUG, чтобы использовать его
  • ~ Фактически это определяется с точки зрения настроек оптимизации, а не конфигурации сборки Xcode.
  • ✗ Недокументированное, что означает, что функция может быть удалена при любом обновлении (но она должна быть безопасной с AppStore, поскольку оптимизатор превратит их в константы)

  • ✗ Использование в if/else всегда будет генерировать предупреждение "Никогда не будет выполнено".

  • 1
    Эти встроенные функции оцениваются во время компиляции или во время выполнения?
  • 0
    @MattDiPasquale Время оптимизации. if _isDebugAssertConfiguration() будет оцениваться как if false в режиме выпуска и if true - режим отладки.
Показать ещё 7 комментариев
49

Xcode 8 и выше

Используйте Условия активной компиляции в Параметры сборки/Swift-компилятор - Пользовательские флаги.

  • Это новый параметр сборки для передачи условных флагов компиляции компилятору Swift.
  • Простые добавления таких флагов: ALPHA, BETA и т.д.

Затем проверьте его с помощью условий компиляции следующим образом:

#if ALPHA
    //
#elseif BETA
    //
#else
    //
#endif

Совет. Вы также можете использовать #if !ALPHA и т.д.

34

Мои два цента для Xcode 8:

a) Пользовательский флаг с использованием префикса -D работает отлично, но...

b) Более простое использование:

В Xcode 8 появился новый раздел: "Активные условия компиляции",  уже с двумя строками, для отладки и выпуска.

Просто добавьте свой параметр WITHOUT -D.

  • 0
    Спасибо за упоминание, что есть две строки для отладки и выпуска
  • 0
    кто-нибудь проверял это в релизе?
Показать ещё 3 комментария
1

Моя концепция основывается на ответе kennytm

Основное преимущество при сравнении двух заключается в том, что это не зависит от частных или недокументированных методов.

В Swift 4:

let isDebug: Bool = {
    var isDebug = false
    // function with a side effect and Bool return value that we can pass into assert()
    func set(debug: Bool) -> Bool {
        isDebug = debug
        return isDebug
    }
    // assert:
    // "Condition is only evaluated in playgrounds and -Onone builds."
    // so isDebug is never changed to false in Release builds
    assert(set(debug: true))
    return isDebug
}()

По сравнению с макросами препроцессора и ответом kennytm,

  • ✓ Вам не нужно определять пользовательский флаг -D DEBUG, чтобы использовать его
  • ~ Фактически это определяется с точки зрения настроек оптимизации, а не конфигурации сборки Xcode.
  • Документированный, что означает, что функция будет следовать нормальным шаблонам выпуска/устаревания API.

  • ✓ Использование в if/else будет не генерировать предупреждение "Никогда не будет выполнено".

Ещё вопросы

Сообщество Overcoder
Наверх
Меню