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

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

#ifdef DEBUG
    // Debug-only code
#endif

Есть ли подобное решение в Swift?


person mxg    schedule 02.06.2014    source источник
comment
В качестве идеи вы можете поместить это в свои заголовки моста obj-c ..   -  person Matej    schedule 10.01.2015
comment
Вам действительно стоит дать ответ, так как у вас есть несколько вариантов на выбор, и этот вопрос получил много голосов.   -  person David H    schedule 04.08.2016
comment
@Userthatisnotauser, вы полностью упустили суть. Вы задаете вопрос, получаете отличные ответы - выберите один. Не игнорируйте время и усилия.   -  person David H    schedule 11.06.2020
comment
@DavidH Нет, на самом деле все наоборот. Мой комментарий был просто упоминанием автостопом о 42-м. Я полностью согласен и хочу проголосовать за него, но я не могу заставить себя попасть на 43-е место.   -  person ReinstateMonica3167040    schedule 11.06.2020
comment
@Userthatisnotauser у этого плаката 19 тысяч баллов - люди проголосовали за его ответы, но, похоже, его не волнуют люди, которые ему помогают. Я всегда выбираю ответ.   -  person David H    schedule 11.06.2020
comment
Проверьте их аккаунт, он мертв.   -  person user2875404    schedule 09.07.2020


Ответы (17)


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

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

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

Однако теперь вы должны установить символ «DEBUG» в другом месте. Установите его в разделе «Компилятор Swift - Пользовательские флаги» в строке «Другие флаги Swift». Вы добавляете символ DEBUG с записью -D DEBUG.

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

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

Вы можете прочитать мой исходный пост здесь.


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

person Jean Le Moignan    schedule 11.06.2014
comment
Это правильный ответ, хотя следует отметить, что вы можете проверить только наличие флага, но не конкретное значение. - person Charles Harley; 18.06.2014
comment
Что ж, неприятно. Я часто использую #if 0 / # else / # endif, чтобы попробовать тестовый код. Это делает тривиальным переходить туда и обратно, чтобы проверить такие вещи, как прочность или скорость. Я так понимаю, препроцессор был просто очередным взломом (взломом на взломе). Но в данном случае это действительно была Полезная Вещь®. - person Lloyd Sargent; 24.03.2015
comment
Дополнительное примечание. Помимо добавления -D DEBUG, как указано выше, вам также необходимо определить DEBUG=1 в Apple LLVM 6.0 - Preprocessing - ›Preprocessor Macros. - person MLQ; 26.03.2015
comment
Можно ли это использовать, чтобы убедиться, что некоторый код (учетные данные для отладки) не встроен в выпущенное приложение при сборке с разными конфигурациями? - person Sunkas; 05.05.2015
comment
@LloydSargent, #if false можно использовать вместо #if 0 - person Zmey; 03.07.2015
comment
Я не мог заставить это работать, пока не изменил форматирование на -DDEBUG из этого ответа: stackoverflow.com/a/24112024/747369. - person Kramer; 23.07.2015
comment
У меня проблема с этим решением или, может быть, причуда. Работает, когда я бездельничаю перед релизом. Я могу настроить свою схему для компиляции либо для отладки, либо для выпуска, и #if DEBUG дает правильный результат. НО, когда я архивирую и отправляю приложение в App Store, оно появляется в магазине в режиме отладки, и это несмотря на то, что моя конфигурация сборки архива установлена ​​на Release, что я подтвердил. Я думаю, что это может не сработать в магазине, как думают люди. Кто-нибудь еще это подтвердит? - person John Bushnell; 12.09.2015
comment
Если вы используете .xcconfig файлы, вы можете настроить этот макрос следующим образом: OTHER_SWIFT_FLAGS = $(inherited) "-D" "MAC_APP_STORE". - person Valentin Shergin; 21.10.2015
comment
@MattQuiros Нет необходимости добавлять DEBUG=1 в Preprocessor Macros, если вы не хотите использовать его в коде Objective-C. - person derpoliuk; 16.11.2015
comment
@BigRon В итоге я установил логическое значение в своей структуре самого высокого уровня из моего основного проекта (где надежен #if DEBUG), а затем весь мой код в моих фреймворках проверяет логическое значение вместо использования #if DEBUG. Конечно, это означает, что все остальные пути кода все еще присутствуют в выпуске, но просто никогда не выполняются. Я не нашел другого решения, но в последнее время не проверял, могла ли Apple сделать что-нибудь, чтобы исправить это. - person John Bushnell; 30.03.2016
comment
@John Несколько дней назад я реализовал это решение отладки без вашего предложения. Я дам вам знать, как это будет происходить. - person BigRon; 30.03.2016
comment
Это сработало в нашем целевом приложении, но не в ... AppTests, хотя мы добавили их в Apple LLVM 6.0 - Preprocessing - ›Preprocessor Macros, а также в Swift Compiler - Custom Flags - person finneycanhelp; 06.04.2016
comment
Есть еще что-то вроде #ifndef DEBUG? (если не отладка ...) - person Daniel; 22.04.2016
comment
@Daniel Вы можете использовать стандартные логические операторы (например: `#if! DEBUG`) - person Jean Le Moignan; 22.04.2016
comment
форматирование -D DEBUG (с пробелом) или -DDEBUG (без пробела)? если вам также нужно определить DEBUG = 1, как сказал @MattQuiros, не следует ли обновлять ответ? - person Crashalot; 12.05.2016
comment
@MattQuiros вроде работает без определения DEBUG=1 в Preprocessor Macros? ты уверен, что это все еще так? - person Crashalot; 12.05.2016
comment
Чтобы прояснить этот флаг: наименование DEBUG является обычным, а не чем-то, что предопределено Xcode или препроцессором. Установите -DDEBUG для конфигурации отладки в других флагах Swift. Установите что-нибудь еще, например -DRELEASE для конфигурации выпуска. - person ff10; 26.07.2016
comment
Версия без пробела (-DDEBUG) мне подходит, но в этом трюке недостает того, что вам нужно добавить это в раздел Swift Compiler - Custom Flags Target, а не в раздел этого имя настроек Project. - person J. Cocoe; 10.09.2016
comment
Это -D Debug для проектов Objective C и D- Debug для проектов Swift. - person harmeet07; 03.10.2016
comment
Помните, что это должно быть определено для конкретной платформы / расширения, которые его используют! Итак, если у вас есть расширение keyboard / today, определите его там. Если у вас другой фреймворк, то же самое. Это может быть необходимо только в том случае, если основной целью является цель c ... - person Warpzit; 21.10.2016
comment
Что касается Xcode 9 GM, мне не нужно было добавлять какие-либо флаги в настройки сборки, поскольку я видел, что DEBUG уже был включен в Active Compilation Conditions в Swift Compiler-Custom Flags. Однако я не уверен, так ли это по умолчанию (когда вы создаете новый проект) или было помещено туда с помощью CocoaPods. - person Stunner; 13.09.2017
comment
@Stunner Это не размещено CocoaPods. Я использую Карфаген, и здесь флаг. Кажется, это значение по умолчанию с Xcode 9. - person Benjamin; 07.01.2018
comment
как вы используете значение для, поскольку оно определено внутри блока - person Swati; 21.03.2018
comment
Это НЕ определения препроцессора, и Swift не имеет препроцессора. Это атрибуты времени компиляции и атрибуты конфигурации сборки. - person Motti Shneor; 18.06.2018
comment
Как я могу проверить, работает ли это? В Objective-C я использовал sth в коде, который выдает ошибку компилятора, когда он находится в правильном разделе, но здесь это больше не работает - person Stephan Boner; 25.07.2018
comment
но это очень уродливое решение, лучше использовать if _isDebugAssertConfiguration() { ... }, это больше похоже на if (BuildConfig.DEBUG) { ... } Андорида - person user924; 30.04.2019

Как указано в Apple Документы

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

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

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

Вот как вы проверяете цель:

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

введите здесь описание изображения

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

person Andrej    schedule 08.04.2016
comment
1. с пробелами тоже работают, 2. следует установить флаг только для отладки? - person c0ming; 25.04.2016
comment
@ c0ming это зависит от ваших потребностей, но если вы хотите, чтобы что-то происходило только в режиме отладки, а не в выпуске, вам нужно удалить -DDEBUG из Release. - person cdf1982; 17.07.2016
comment
После того, как я установил собственный флаг -DLOCAL на моем #if LOCAl #else #endif, он попадает в раздел #else. Я продублировал исходную цель AppTarget и переименовал ее в AppTargetLocal и установил свой собственный флаг. - person Perwyl Liu; 20.07.2016
comment
@PerwylLiu Да, таким образом вы можете установить разные флаги для разных целей, если у вас их несколько. Только не забудьте установить флажки для других целей. - person Andrej; 20.07.2016
comment
@Andrej, а вы знаете, как заставить XCTest распознавать и настраиваемые флаги? Я понимаю, что он попадает в #if LOCAL , ожидаемый результат, когда я запускаю симулятор, и падает в #else во время тестирования. Я хочу, чтобы он тоже попал в #if LOCAL во время тестирования. - person Perwyl Liu; 20.07.2016
comment
Это должен быть принятый ответ. Текущий принятый ответ неверен для Swift, поскольку он применим только к Objective-C. - person miken.mkndev; 03.09.2016
comment
@Andrej, не могли бы вы отредактировать ответ, чтобы использовать Active Compilation Conditions вместо Other Swift Flags. Теперь есть более детальный контроль на основе конфигурации. - person user1046037; 02.08.2017
comment
Также нет необходимости в префиксе D, если вы используете Active Compilation Conditions - person user1046037; 02.08.2017

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

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

введите описание изображения здесь

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

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

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

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

person matt    schedule 21.06.2014
comment
По какой-то причине моя переменная среды вернулась как nil при втором запуске приложения - person Eugene; 25.07.2014
comment
Осторожно: переменные среды устанавливаются для всех конфигураций сборки, они не могут быть установлены для отдельных. Так что это не жизнеспособное решение, если вам нужно изменить поведение в зависимости от того, является ли это выпуском или отладочной сборкой. - person Eric; 10.06.2015
comment
@Eric Согласен, но они установлены не для всех действий схемы. Таким образом, вы можете делать одно при сборке и запуске, а другое - о архиве, что часто является реальным различием, которое вы хотите провести. Или у вас может быть несколько схем, что также является общим шаблоном в реальной жизни. Кроме того, как я сказал в своем ответе, переключать переменные среды в схеме очень просто. - person matt; 10.06.2015
comment
@matt: Понятно. Но как установить переменную среды для действия схемы архива? Я не вижу там вкладки Аргументы ... (Xcode 6.3.2) - person Eric; 10.06.2015
comment
@Eric добавил к моему ответу реальный пример (на самом деле он появился вчера). - person matt; 11.06.2015
comment
@Eric, но можно создавать разные схемы (например, две или более на цель) с разными переменными среды, верно? - person Dan Rosenstark; 17.07.2015
comment
Переменные среды НЕ работают в режиме архива. Они применяются только тогда, когда приложение запускается из XCode. Если вы попытаетесь получить к ним доступ на устройстве, приложение выйдет из строя. Узнал на собственном горьком опыте. - person iupchris10; 15.09.2015
comment
@ iupchris10 В архиве нет переменных среды - это последние слова моего ответа выше. Это, как я сказал в своем ответе, хорошо. Это точка. - person matt; 15.09.2015
comment
Это как раз правильное решение для случая XCTest, когда вам нужно поведение по умолчанию, когда приложение работает в симуляторе, но вы хотите строго контролировать поведение в тестах. - person Stan; 02.06.2017
comment
Относится к комментарию @ iupchris10 - я хотел бы уточнить, что в вашем проекте вы можете оставить код, который пытается получить доступ к переменным среды, даже если вы заархивируете приложение. Когда вы запускаете приложение, которое было заархивировано, вы получаете nil для ожидаемого String?. Так что, если вы правильно обработаете необязательный результат, ваше приложение не выйдет из строя. Вот фрагмент кода Xcode9: let myCustomVar = ProcessInfo.processInfo.environment["MY_CUSTOM_VAR"]. myCustomVar. - person Andrej; 25.01.2018
comment
@Andrej Я думаю, что это то, о чем говорится в моем ответе (иллюстрируется с помощью if dic["TRIPLE"] != nil), но спасибо, что подчеркнули эту мысль. - person matt; 25.01.2018
comment
extension ProcessInfo {var isDebug: Bool {get {return self.environment [DEBUG] == 1}}} - person Benny Davidovitz; 13.01.2019
comment
У Swift есть флаг компилятора, который вы можете использовать, чтобы узнать, запущено ли приложение на симуляторе: #if targetEnvironment(simulator) - person Peter Schorn; 05.08.2020
comment
@PeterSchorn Верно. Мой ответ гласит, что иногда это не то, что нужно. То, что вы говорите, правда и то, что я говорю, правда. Разве жизнь не велика? - person matt; 05.08.2020
comment
@matt Я не имел в виду, что мой ответ противоречит всему, что вы сказали. Я согласен со всем, что вы сказали. Мой ответ чисто аддитивный. Я просто не видел, чтобы этот флаг компилятора упоминался где-либо еще, поэтому я решил опубликовать комментарий об этом. - person Peter Schorn; 05.08.2020
comment
Недостаточно людей осознают эффективность и разветвления рабочего процесса добавления миллиона требований повторной компиляции ... спасибо за этот @matt - person Alex Moore; 20.11.2020

Существенное изменение замены ifdef связано с Xcode 8. то есть с использованием условий активной компиляции.

См. Создание и связывание в Информация о выпуске Xcode 8.

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

Новая настройка: 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 в новую настройку. Пора переместить все эти значения условной компиляции!

Пожалуйста, обратитесь к ссылке ниже, чтобы узнать больше о функции Swift Build Settings в Xcode 8: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-анализатор-улучшения/

person DShah    schedule 11.09.2016
comment
Есть ли способ отключить набор активных условий компиляции во время сборки? Мне нужно отключить условие DEBUG при создании конфигурации отладки для тестирования. - person Jonny; 01.09.2017
comment
@Jonny Единственный способ, который я нашел, - это создать конфигурацию третьей сборки для проекта. На вкладке «Проект» ›« Информация »› «Конфигурации» нажмите «+», затем продублируйте «Отладка». Затем вы можете настроить активные условия компиляции для этой конфигурации. Не забудьте отредактировать свою цель ›Тестовые схемы, чтобы использовать новую конфигурацию сборки! - person matthias; 11.10.2017
comment
Это должен быть правильный ответ ... это единственное, что у меня сработало на xCode 9 с использованием Swift 4.x! - person shokaveli; 12.04.2018
comment
Кстати, в Xcode 9.3 Swift 4.1 DEBUG уже присутствует в активных условиях компиляции, и вам не нужно ничего добавлять для проверки конфигурации DEBUG. Просто #if DEBUG и #endif. - person Denis Kutlubaev; 03.06.2018
comment
Я думаю, что это и не по теме, и плохой поступок. вы не хотите отключать активные условия компиляции. для тестирования вам понадобится новая и отличная конфигурация - на ней НЕ будет тега Debug. Узнай о схемах. - person Motti Shneor; 18.06.2018

Начиная с Swift 4.1, если все, что вам нужно, это просто проверить, построен ли код с конфигурацией отладки или выпуска, вы можете использовать встроенные функции:

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

e.g.

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

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

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

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

person kennytm    schedule 30.12.2015
comment
Оцениваются ли эти встроенные функции во время компиляции или выполнения? - person ma11hew28; 28.02.2016
comment
@MattDiPasquale Время оптимизации. if _isDebugAssertConfiguration() будет оцениваться как if false в режиме выпуска, а if true в режиме отладки. - person kennytm; 01.03.2016
comment
Однако я не могу использовать эти функции, чтобы отключить некоторые переменные, предназначенные только для отладки в выпуске. - person Franklin Yu; 12.04.2016
comment
Эти функции где-то задокументированы? - person Tom Harrington; 02.05.2016
comment
@TomHarrington Это нигде не было публично задокументировано. Исходный код находится в github.com/apple/swift / blob / master / stdlib / public / core /. Существует черновик предложения SE о замене этих функций на лучший синтаксис, например #if config(debug). - person kennytm; 13.06.2016
comment
Начиная с Swift 3.0 и XCode 8, эти функции недействительны. - person CodeBender; 04.11.2016
comment
@CodeBender В Swift 3.1 только _isReleaseAssertConfiguration() недействителен, два других снова становятся общедоступными из-за ошибки . - person kennytm; 26.05.2017
comment
@kennytm Знаете ли вы, доступны ли эти функции в Swift 4 и новее? не могли бы вы обновить свой ответ? - person Motti Shneor; 18.06.2018
comment
@MottiShneor все еще существует в версии 4.1. Обновлено. - person kennytm; 18.06.2018

Xcode 8 и выше

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

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

Затем проверьте его с помощью условия компиляции, например:

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

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

person Jakub Truhlář    schedule 13.01.2017

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

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

Например:

#if os(iOS)
    let color = UIColor.redColor()
#else
    let color = NSColor.redColor()
#endif
person rickster    schedule 02.06.2014
comment
Во-первых, произвольная подстановка кода нарушает безопасность типов и память. Разве препроцессор не выполняет свою работу раньше, чем компилятор (отсюда и название)? Так что все эти проверки еще могут иметь место. - person Thilo; 04.06.2014
comment
@Thilo Я думаю, что это ломает, так это поддержка IDE - person Aleksandr Dubinsky; 05.06.2014
comment
Я думаю, что @rickster имеет в виду, что макросы препроцессора C не понимают типа, и их присутствие нарушит требования Swift к типу. Причина, по которой макросы работают в C, заключается в том, что C допускает неявное преобразование типов, что означает, что вы можете поместить свой INT_CONST в любое место, где float будет принят. Свифт этого не допустил. Кроме того, если бы вы могли сделать var floatVal = INT_CONST, это неизбежно сломалось бы где-нибудь позже, когда компилятор ожидает Int, но вы используете его как Float (тип floatVal будет выведен как Int). 10 забросов спустя, и его просто очистить, чтобы удалить макросы ... - person Ephemera; 07.06.2014
comment
Я пытаюсь использовать это, но похоже, что это не работает, он все еще компилирует код Mac в сборках iOS. Есть ли где-нибудь еще один экран настройки, который нужно настроить? - person Maury Markowitz; 22.02.2015
comment
@Thilo, вы правы - препроцессор не нарушает безопасность типов или памяти. - person tcurdt; 10.06.2016
comment
Вы можете думать об этом как о причудливом поиске и замене / создании шаблонов перед этапом компиляции. Учитывая, что это преобразование на уровне источника, оно ломает вещи только в том случае, если результат преобразования ломает вещи. Вот почему пример и вывод @ Ephemera в этом отношении неверны. - person tcurdt; 10.06.2016
comment
Кажется, это единственное решение, которое работает с Xcode 10.1 Swift 4.2! - person nickgzzjr; 29.01.2019

Константа isDebug на основе условий активной компиляции

Другое, возможно, более простое решение, которое по-прежнему приводит к логическому значению, которое вы можете передавать в функции без добавления #if условных обозначений по всей кодовой базе, - это определить DEBUG как одну из целей сборки вашего проекта Active Compilation Conditions и включить следующее (я определяю его как глобальную константу) :

#if DEBUG
    let isDebug = true
#else
    let isDebug = false
#endif

Константа isDebug на основе настроек оптимизации компилятора

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

Основное преимущество при сравнении с 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 true in Release builds
    assert(set(debug: true))
    return isDebug
}()

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

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

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

person Jon Willis    schedule 22.11.2017

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

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

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

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

Просто добавьте свое определение БЕЗ -D.

person ingconti    schedule 04.09.2016
comment
Спасибо, что упомянули, что есть ДВА РЯДА ДЛЯ ОТЛАДКИ И ВЫПУСКА - person Yitzchak; 13.11.2016
comment
кто-нибудь тестировал это в выпуске? - person Glenn Posadas; 21.11.2016
comment
Это обновленный ответ для быстрых пользователей. т.е. без -D. - person Mani; 19.12.2017
comment
Я пытался установить флаг в Other Swift Flags, но ничего не вышло. Спасибо за ваше предложение установить его в активных условиях компиляции. Оно работает. - person Nguyễn Anh Tuấn; 12.09.2020

Moignans answer здесь отлично работает. Вот еще одна информация на случай, если это поможет,

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

Вы можете отменить макросы, как показано ниже,

#if !RELEASE
    let a = 2
#else
    let a = 3
#endif
person Sazzad Hissain Khan    schedule 21.05.2019

В проектах Swift, созданных с помощью Xcode версии 9.4.1, Swift 4.1

#if DEBUG
#endif

работает по умолчанию, потому что в макросах препроцессора DEBUG = 1 уже установлено Xcode.

Таким образом, вы можете использовать #if DEBUG "из коробки".

Кстати, как использовать блоки компиляции условий в целом написано в книге Apple The Swift Programming Language 4.1 (раздел Compiler Control Statements), а как писать флаги компиляции и что является аналогом макросов C в Swift написано в другая книга Apple «Использование Swift с Cocoa и Objective C» (в разделе «Директивы препроцессора»)

Надеюсь, что в будущем Apple напишет более подробное содержание и указатели для своих книг.

person Vadim Motorine    schedule 08.09.2018

XCODE 9 И ВЫШЕ

#if DEVELOP
    //
#elseif PRODCTN
    //
#else
    //
#endif
person midhun p    schedule 14.09.2018

Есть несколько процессоров, которые возражают, и я перечислил их ниже. вы можете изменить аргумент по своему усмотрению:

#if os(macOS) /* Checks the target operating system */

#if canImport(UIKit) /* Check if a module presents */

#if swift(<5) /* Check the Swift version */

#if targetEnvironment(simulator) /* Check envrionments like Simulator or Catalyst */

#if compiler(<7) /* Check compiler version */

Кроме того, вы можете использовать любые настраиваемые флаги, такие как DEBUG, или любые другие флаги, которые вы определили.

#if DEBUG
print("Debug mode")
#endif
person Mojtaba Hosseini    schedule 05.08.2020

После установки DEBUG=1 в ваших GCC_PREPROCESSOR_DEFINITIONS Настройках сборки я предпочитаю использовать функцию для выполнения этих вызовов:

func executeInProduction(_ block: () -> Void)
{
    #if !DEBUG
        block()
    #endif
}

А затем просто включите в эту функцию любой блок, который я хочу опустить в сборках отладки:

executeInProduction {
    Fabric.with([Crashlytics.self]) // Compiler checks this line even in Debug
}

Преимущество по сравнению с:

#if !DEBUG
    Fabric.with([Crashlytics.self]) // This is not checked, may not compile in non-Debug builds
#endif

Это компилятор проверяет синтаксис моего кода, поэтому я уверен, что его синтаксис правильный и строит.

person Rivera    schedule 07.02.2018

! [В Xcode 8 и выше перейдите к настройке сборки -> найдите пользовательские флаги] 1

В коде

 #if Live
    print("Live")
    #else
    print("debug")
    #endif
person sachin_kvk    schedule 21.02.2018
comment
Вы здесь наткнулись! Swift #if смотрит на настраиваемые флаги, а не на макросы препроцессора. Пожалуйста, обновите свой ответ содержанием ссылки, часто ссылки через некоторое время перестают работать. - person Dale; 18.05.2019

func inDebugBuilds(_ code: () -> Void) {
    assert({ code(); return true }())
}

Источник

person Adam Smaka    schedule 30.11.2018
comment
Это не условная компиляция. Хотя это полезно, это всего лишь простая старая условная среда выполнения. OP запрашивает время компиляции для целей метапрограммирования - person Shayne; 18.01.2019
comment
Просто добавьте @inlinable перед func, и это будет самый элегантный и идиоматичный способ для Swift. В сборках релизов ваш code() блок будет оптимизирован и полностью исключен. Аналогичная функция используется в собственной структуре NIO от Apple. - person mojuba; 12.05.2019

Это основано на ответе Джона Уиллиса, который полагается на assert, который выполняется только в компиляциях Debug:

func Log(_ str: String) { 
    assert(DebugLog(str)) 
}
func DebugLog(_ str: String) -> Bool { 
    print(str) 
    return true
}

Мой вариант использования - регистрация операторов печати. Вот тест для версии Release на iPhone X:

let iterations = 100_000_000
let time1 = CFAbsoluteTimeGetCurrent()
for i in 0 ..< iterations {
    Log ("⧉ unarchiveArray:\(fileName) memoryTime:\(memoryTime) count:\(array.count)")
}
var time2 = CFAbsoluteTimeGetCurrent()
print ("Log: \(time2-time1)" )

печатает:

Log: 0.0

Похоже, Swift 4 полностью исключает вызов функции.

person Warren Stringer    schedule 15.12.2017
comment
Устраняет, например, полностью удаляет вызов, когда не выполняется отладка - из-за того, что функция пуста? Это было бы замечательно. - person Johan; 24.04.2018