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

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

#ifdef DEBUG
    // Debug-only code
#endif

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

19 ответов

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

Серьезное изменение 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 в Xcode 8: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-analyzer-improvements/

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

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

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

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

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

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

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

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

например

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

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

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

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

Xcode 8 и выше

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

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

Затем проверьте это с условиями компиляции, как это:

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

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

Нет препроцессора Swift. (С одной стороны, произвольная замена кода нарушает безопасность типов и памяти.)

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

Например:

#if os(iOS)
    let color = UIColor.redColor()
#else
    let color = NSColor.redColor()
#endif

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

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

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

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

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

Основное преимущество по сравнению с 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 не будет генерировать предупреждение "никогда не будет выполнено".

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

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

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

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

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

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

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

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

#if !RELEASE
    let a = 2
#else
    let a = 3
#endif

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

#if DEBUG
#endif

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

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

Между прочим, как использовать блоки компиляции условий в общем, написано в книге Apple "Язык программирования Swift 4.1" (раздел "Операции управления компилятором"), а как написать флаги компиляции и то, что является аналогом макросов C в Swift, написано в другая книга Apple, использующая Swift с какао и Objective C (в разделе Директивы препроцессора)

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

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

#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

XCODE 9 И ВЫШЕ

#if DEVELOP
    //
#elseif PRODCTN
    //
#else
    //
#endif

После настройки DEBUG=1 в вашем 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

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

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

Источник

Это основано на ответе Джона Уиллиса, основанном на утверждении, которое выполняется только в отладочных компиляциях:

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 полностью исключает вызов функции.

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

      enum AppConfiguration: String {
    
    case debug
    case release
    
}

поместите это в статическую переменную

      struct Constants {
    
    static var appConfiguration: AppConfiguration {
        
        #if DEBUG
        return .debug
        #else
        return .release
        #endif
        
    }
    
}

который вы затем можете использовать во всем своем проекте следующим образом:

      if Constants.appConfiguration == .debug {

print("debug")

} else {

print("release")

}

Обновление Swift 5 для ответа мэтта

      let dic = ProcessInfo.processInfo.environment
if dic["TRIPLE"] != nil {
// ... do your secret stuff here ...
}
Другие вопросы по тегам