#ifdef замена на языке Swift
В C/С++/Objective-C вы можете определить макрос, используя препроцессоры компилятора.
Более того, вы можете включать/исключать некоторые части кода с помощью препроцессоров компилятора.
#ifdef DEBUG
// Debug-only code
#endif
Есть ли аналогичное решение в Swift?
Ответы
Ответ 1
Да, вы можете это сделать.
В 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
. Кажется, компилятор игнорирует флаг с определенным значением.
Ответ 2
Как указано в Apple Docs
Компилятор Swift не включает препроцессор. Вместо этого он использует преимущества атрибутов компиляции, конфигурации сборки и языковых функций для выполнения той же функциональности. По этой причине директивы препроцессора не импортируются в Swift.
Мне удалось добиться того, что я хотел, используя пользовательские конфигурации сборки:
- Перейдите в свой проект/выберите цель/Настройки сборки/поиск пользовательских флагов
- Для выбранной вами цели установите свой собственный флаг с использованием префикса -D (без пробелов), как для отладки, так и для выпуска
- Выполняйте шаги выше для каждой вашей цели.
Здесь вы проверяете цель:
#if BANANA
print("We have a banana")
#elseif MELONA
print("Melona")
#else
print("Kiwi")
#endif
![введите описание изображения здесь]()
Протестировано с использованием Swift 2.2
Ответ 3
Во многих ситуациях вам не нужна условная компиляция; вам просто нужно условное поведение, которое вы можете включить и выключить. Для этого вы можете использовать переменную окружения. Это имеет огромное преимущество, которое вам не нужно перекомпилировать.
Вы можете установить переменную окружения и легко включить или отключить ее в редакторе схем:
![enter image description here]()
Вы можете получить переменную окружения с помощью NSProcessInfo:
let dic = NSProcessInfo.processInfo().environment
if dic["TRIPLE"] != nil {
// ... do secret stuff here ...
}
Вот пример реальной жизни. Мое приложение работает только на устройстве, потому что оно использует музыкальную библиотеку, которая не существует на Симуляторе. Как же тогда снимать скриншоты на Симуляторе для устройств, которых у меня нет? Без этих снимков экрана я не могу отправить AppStore.
Мне нужны поддельные данные и другой способ его обработки. У меня есть две переменные среды: одна, которая при включении сообщает приложению, чтобы генерировать поддельные данные из реальных данных во время работы на моем устройстве; другой, который при включении использует поддельные данные (а не отсутствующую музыкальную библиотеку) во время работы на симуляторе. Переключение каждого из этих специальных режимов вкл/выкл легко благодаря флагом переменной окружения в редакторе Схемы. И бонус заключается в том, что я не могу случайно использовать их в своей сборке App Store, потому что в архиве нет переменных среды.
Ответ 4
Основное изменение замены 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/
Ответ 5
Начиная с Swift 4.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
-
Ocu Недокументированное, что означает, что функция может быть удалена при любом обновлении (но она должна быть безопасной для AppStore, так как оптимизатор превратит их в константы)
-
In Использование в if/else всегда будет генерировать предупреждение "никогда не будет выполнено".
Ответ 6
Xcode 8 и выше
Используйте Условия активной компиляции в Параметры сборки/Swift-компилятор - Пользовательские флаги.
- Это новый параметр сборки для передачи условных флагов компиляции компилятору Swift.
- Простые добавления таких флагов:
ALPHA
, BETA
и т.д.
Затем проверьте его с помощью условий компиляции следующим образом:
#if ALPHA
//
#elseif BETA
//
#else
//
#endif
Совет. Вы также можете использовать #if !ALPHA
и т.д.
Ответ 7
Нет препроцессора Swift. (Во-первых, произвольная замена кода прерывает безопасность типов и памяти.)
Swift также включает параметры конфигурации времени сборки, поэтому вы можете условно включать код для определенных платформ или стилей сборки или в ответ на флаги, которые вы определяете с помощью -D
compiler args. В отличие от C, условно скомпилированный раздел вашего кода должен быть синтаксически завершен. Вот раздел об этом в Использование Swift с Cocoa и Objective-C.
Например:
#if os(iOS)
let color = UIColor.redColor()
#else
let color = NSColor.redColor()
#endif
Ответ 8
Мои два цента для Xcode 8:
a) Пользовательский флаг с использованием префикса -D
работает отлично, но...
b) Более простое использование:
В Xcode 8 появился новый раздел: "Активные условия компиляции",
уже с двумя строками, для отладки и выпуска.
Просто добавьте свой параметр WITHOUT -D
.
Ответ 9
Константа isDebug в зависимости от условий активной компиляции
Другое, возможно, более простое, решение, которое все еще приводит к логическому значению, которое вы можете передавать в функции без #if
условных обозначений #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 не будет генерировать предупреждение "никогда не будет выполнено".
Ответ 10
В проектах Swift, созданных с версией Xcode 9.4.1, Swift 4.1
#if DEBUG
#endif
работает по умолчанию, потому что в макросах препроцессора DEBUG = 1 уже был установлен Xcode.
Так что вы можете использовать #if DEBUG "из коробки".
Между прочим, как использовать блоки компиляции условий в общем, написано в книге Apple "Язык программирования Swift 4.1" (раздел "Операции управления компилятором"), а как написать флаги компиляции и что является аналогом макросов C в Swift, написано в другая книга Apple, использующая Swift с cocoa и Objective C (в разделе Директивы препроцессора)
Надеюсь, что в будущем Apple напишет более подробное содержание и индексы для своих книг.
Ответ 11
XCODE 9 И ВЫШЕ
#if DEVELOP
//
#elseif PRODCTN
//
#else
//
#endif
Ответ 12
После установки DEBUG=1
в ваших GCC_PREPROCESSOR_DEFINITIONS
сборки GCC_PREPROCESSOR_DEFINITIONS
я предпочитаю использовать функцию для выполнения этих вызовов:
func executeInProduction(_ block: () -> Void)
{
#if !DEBUG
block()
#endif
}
А затем просто включите в эту функцию любой блок, который я хочу опустить в сборках Debug:
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
Это то, что компилятор проверяет синтаксис моего кода, поэтому я уверен, что его синтаксис правильный и строит.
Ответ 13
! [В Xcode 8 и выше перейдите к настройке сборки → поиск пользовательских флагов] 1
В коде
#if Live
print("Live")
#else
print("debug")
#endif
Ответ 14
Это основано на ответе Джона Уиллиса, основанном на утверждении, которое выполняется только в отладочных компиляциях:
func Log(_ str: String) {
assert(DebugLog(str))
}
func DebugLog(_ str: String) -> Bool {
print(str)
return true
}
Мой вариант использования для регистрации операторов печати. Вот эталонный тест для релизной версии на 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 полностью исключает вызов функции.
Ответ 15
Ответ Moignans здесь отлично работает. Вот еще одна информация, если это поможет,
#if DEBUG
let a = 2
#else
let a = 3
#endif
Вы можете отменить макросы, как показано ниже,
#if !RELEASE
let a = 2
#else
let a = 3
#endif
Ответ 16
func inDebugBuilds(_ code: () -> Void) {
assert({ code(); return true }())
}
Источник