用Swift语言替换#ifdef

时间:2014-06-02 21:06:37

标签: swift xcode preprocessor preprocessor-directive

在C / C ++ / Objective-C中,您可以使用编译器预处理器定义宏。 此外,您可以使用编译器预处理器包含/排除代码的某些部分。

#ifdef DEBUG
    // Debug-only code
#endif

Swift中是否有类似的解决方案?

16 个答案:

答案 0 :(得分:961)

是的,你可以做到。

在Swift中,你仍然可以按照Apple docs使用“#if /#else /#endif”预处理器宏(尽管更受约束)。这是一个例子:

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

现在,您必须在其他位置设置“DEBUG”符号。将它设置在“Swift Compiler - Custom Flags”部分,“Other Swift Flags”行中。您可以使用-D DEBUG条目添加DEBUG符号。

像往常一样,您可以在Debug或Release中设置不同的值。

我在实际代码中对它进行了测试并且它有效;但它似乎并没有在游乐场中得到认可。

您可以阅读我的原始帖子here


重要提示: -DDEBUG=1不起作用。只有-D DEBUG有效。似乎编译器忽略了具有特定值的标志。

答案 1 :(得分:326)

Apple Docs

中所述
  

Swift编译器不包含预处理器。相反,它利用编译时属性,构建配置和语言功能来实现相同的功能。因此,预处理程序指令不会在Swift中导入。

我已经设法通过使用自定义构建配置实现了我想要的目标:

  1. 转到您的项目/选择目标/构建设置/搜索自定义标志
  2. 对于您选择的目标,使用-D前缀(不带空格)为Debug和Release
  3. 设置自定义标志
  4. 为您拥有的每个目标执行上述步骤
  5. 以下是检查目标的方法:

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

    enter image description here

    使用Swift 2.2进行测试

答案 2 :(得分:160)

在许多情况下,您并不需要有条件的编译;您只需要可以打开和关闭的条件行为。为此,您可以使用环境变量。这具有巨大的优势,您实际上不必重新编译。

您可以在方案编辑器中设置环境变量,并轻松打开或关闭它:

enter image description here

您可以使用NSProcessInfo检索环境变量:

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

这是一个真实的例子。我的应用程序仅在设备上运行,因为它使用的音乐库在模拟器上不存在。那么,如何在模拟器上为我不拥有的设备拍摄屏幕截图?没有这些屏幕截图,我无法提交给AppStore。

我需要虚假数据不同的处理方式。我有两个环境变量:一个在打开时告诉应用程序在我的设备上运行时从真实数据生成虚假数据;另一个,当打开时,在模拟器上运行时使用假数据(而不是丢失的音乐库)。由于Scheme编辑器中的环境变量复选框,可以轻松打开/关闭每个特殊模式。奖金是我不能在我的App Store构建中意外使用它们,因为归档没有环境变量。

答案 3 :(得分:142)

Xcode 8提出了ifdef替换的重大变化,即使用活动编译条件

请参阅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中的更多Swift Build Settings功能,请参阅以下链接: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-analyzer-improvements/

答案 4 :(得分:86)

从Swift 4.1开始,如果您只需要检查代码是使用调试版还是发布版构建的,那么您可以使用内置函数:

  • _isDebugAssertConfiguration()(当优化设置为-Onone时为真)
  • _isReleaseAssertConfiguration()(当优化设置为-O时为true)(在Swift 3 +上不可用)
  • _isFastAssertConfiguration()(当优化设置为-Ounchecked时为真)

e.g。

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

与预处理器宏相比,

  • ✓您无需定义自定义-D DEBUG标志即可使用
  • 〜它实际上是根据优化设置定义的,而不是Xcode构建配置
  • ✗未记载,这意味着可以在任何更新中删除该函数(但它应该是AppStore安全的,因为优化器会将这些变为常量)

  • ✗在if / else中使用将始终生成“永不执行”警告。

答案 5 :(得分:79)

Xcode 8及以上

构建设置/ Swift编译器 - 自定义标记中使用有效编译条件设置。

  • 这是用于将条件编译标志传递给Swift编译器的新构建设置。
  • 简单添加这样的标记:ALPHABETA等。

然后使用 compilation conditions 进行检查:

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

提示:您还可以使用#if !ALPHA

答案 6 :(得分:73)

没有Swift预处理器。 (一方面,任意代码替换会破坏类型和内存安全性。)

但是,Swift确实包含构建时配置选项,因此您可以有条件地包含某些平台或构建样式的代码,或者响应您使用-D编译器args定义的标志。但是,与C不同,代码的有条件编译部分必须在语法上完整。在Using Swift With Cocoa and Objective-C中有关于此内容的部分。

例如:

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

答案 7 :(得分:45)

我对Xcode 8的两分钱:

a)使用-D前缀的自定义标志可以正常工作,但是......

b)使用更简单:

在Xcode 8中有一个新的部分:" Active Compilation Conditions",  已经有两行,用于调试和发布。

只需添加您的定义WITHOUT -D

答案 8 :(得分:39)

isDebug Constant基于活动编译条件

另一个也许更简单的解决方案仍然会产生一个布尔值,你可以在整个代码库中传递给函数而不需要#if个条件,就是将DEBUG定义为你的项目构建目标之一{{1并包含以下内容(我将其定义为全局常量):

Active Compilation Conditions

isDebug Constant基于编译器优化设置

这个概念建立在kennytm's answer

之上

与kennytm进行比较时的主要优点是,这不依赖于私有或未记录的方法。

Swift 4

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

与预处理器宏和kennytm的回答相比

  • ✓您无需定义自定义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 }() 标志即可使用
  • 〜它实际上是根据优化设置定义的,而不是Xcode构建配置
  • 已记录,表示该功能将遵循正常的API发布/弃用模式。

  • ✓在if / else中使用会生成“永不执行”警告。

答案 9 :(得分:16)

在使用Xcode版本9.4.1,Swift 4.1创建的Swift项目中

#if DEBUG
#endif

默认情况下有效,因为Xcode已经在预处理宏中设置了DEBUG = 1。

因此您可以“开箱即用”使用#if DEBUG。

顺便说一句,一般如何使用条件编译块写在Apple的书《 Swift编程语言4.1》(“编译器控制语句”部分)中,以及如何编写编译标志以及Swift中C宏的对应内容是在另一本苹果的书《将Swift与Cocoa和Objective C结合使用》中编写的(在Preprocessor Directives部分中)

希望将来苹果将为他们的书写出更详细的内容和索引。

答案 10 :(得分:7)

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

是编译器检查我的代码的语法,所以我确信它的语法是正确的并且构建。

答案 11 :(得分:7)

有些处理器接受一个参数,我在下面列出了它们。您可以根据需要更改参数:

#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

答案 12 :(得分:3)

![In Xcode 8 & above go to build setting -> search for custom flags ] 1

代码

 #if Live
    print("Live")
    #else
    print("debug")
    #endif

答案 13 :(得分:1)

这建立在Jon Willis's的答案之上,它依赖于assert,它只在Debug编译中执行:

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完全取消了函数调用。

答案 14 :(得分:0)

Moignans answer在这里工作正常。如果有帮助,这是另一种信息和平,

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

您可以像下面那样否定宏,

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

答案 15 :(得分:-1)

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

Source