В C/С++/Objective-C вы можете определить макрос, используя препроцессоры компилятора. Более того, вы можете включать/исключать некоторые части кода с помощью препроцессоров компилятора.
#ifdef DEBUG
// Debug-only code
#endif
Есть ли аналогичное решение в Swift?
Да, вы можете это сделать.
В 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
. Кажется, компилятор игнорирует флаг с определенным значением.
Как указано в Apple Docs
Компилятор Swift не включает препроцессор. Вместо этого он использует преимущества атрибутов компиляции, конфигурации сборки и языковых функций для выполнения той же функциональности. По этой причине директивы препроцессора не импортируются в Swift.
Мне удалось добиться того, что я хотел, используя пользовательские конфигурации сборки:
Здесь вы проверяете цель:
#if BANANA
print("We have a banana")
#elseif MELONA
print("Melona")
#else
print("Kiwi")
#endif
Протестировано с использованием Swift 2.2
Во многих ситуациях вам не нужна условная компиляция; вам просто нужно условное поведение, которое вы можете включить и выключить. Для этого вы можете использовать переменную окружения. Это имеет огромное преимущество, которое вам не нужно перекомпилировать.
Вы можете установить переменную окружения и легко включить или отключить ее в редакторе схем:
Вы можете получить переменную окружения с помощью NSProcessInfo:
let dic = NSProcessInfo.processInfo().environment
if dic["TRIPLE"] != nil {
// ... do secret stuff here ...
}
Вот пример реальной жизни. Мое приложение работает только на устройстве, потому что оно использует музыкальную библиотеку, которая не существует на Симуляторе. Как же тогда снимать скриншоты на Симуляторе для устройств, которых у меня нет? Без этих снимков экрана я не могу отправить AppStore.
Мне нужны поддельные данные и другой способ его обработки. У меня есть две переменные среды: одна, которая при включении сообщает приложению, чтобы генерировать поддельные данные из реальных данных во время работы на моем устройстве; другой, который при включении использует поддельные данные (а не отсутствующую музыкальную библиотеку) во время работы на симуляторе. Переключение каждого из этих специальных режимов вкл/выкл легко благодаря флагом переменной окружения в редакторе Схемы. И бонус заключается в том, что я не могу случайно использовать их в своей сборке App Store, потому что в архиве нет переменных среды.
Основное изменение замены 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/
Нет препроцессора Swift. (Во-первых, произвольная замена кода прерывает безопасность типов и памяти.)
Swift также включает параметры конфигурации времени сборки, поэтому вы можете условно включать код для определенных платформ или стилей сборки или в ответ на флаги, которые вы определяете с помощью -D
compiler args. В отличие от C, условно скомпилированный раздел вашего кода должен быть синтаксически завершен. Вот раздел об этом в Использование Swift с Cocoa и Objective-C.
Например:
#if os(iOS)
let color = UIColor.redColor()
#else
let color = NSColor.redColor()
#endif
Как и в случае Swift 3.1, если вам нужно всего лишь проверить, построен ли код с настройкой отладки или выпуска, вы можете использовать встроенные функции:
_isDebugAssertConfiguration()
(true, когда оптимизация установлена на -Onone
)_isReleaseAssertConfiguration()
(true, когда оптимизация установлена на -O
)_isFastAssertConfiguration()
(true, когда оптимизация установлена на -Ounchecked
)например.
func obtain() -> AbstractThing {
if _isDebugAssertConfiguration() {
return DecoratedThingWithDebugInformation(Thing())
} else {
return Thing()
}
}
По сравнению с макросами препроцессора,
-D DEBUG
, чтобы использовать его✗ Недокументированное, что означает, что функция может быть удалена при любом обновлении (но она должна быть безопасной с AppStore, поскольку оптимизатор превратит их в константы)
@testable
, судьба неопределенная в Swift 4.0✗ Использование в if/else всегда будет генерировать предупреждение "Никогда не будет выполнено".
if _isDebugAssertConfiguration()
будет оцениваться как if false
в режиме выпуска и if true
- режим отладки.
Используйте Условия активной компиляции в Параметры сборки/Swift-компилятор - Пользовательские флаги.
ALPHA
, BETA
и т.д.Затем проверьте его с помощью условий компиляции следующим образом:
#if ALPHA
//
#elseif BETA
//
#else
//
#endif
Совет. Вы также можете использовать
#if !ALPHA
и т.д.
Мои два цента для Xcode 8:
a) Пользовательский флаг с использованием префикса -D
работает отлично, но...
b) Более простое использование:
В Xcode 8 появился новый раздел: "Активные условия компиляции", уже с двумя строками, для отладки и выпуска.
Просто добавьте свой параметр WITHOUT -D
.
Моя концепция основывается на ответе 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
, чтобы использовать его✓ Документированный, что означает, что функция будет следовать нормальным шаблонам выпуска/устаревания API.
✓ Использование в if/else будет не генерировать предупреждение "Никогда не будет выполнено".