用Swift语言替换#ifdef

mxg*_*mxg 697 xcode preprocessor preprocessor-directive swift

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

#ifdef DEBUG
    // Debug-only code
#endif
Run Code Online (Sandbox Code Playgroud)

Swift中有类似的解决方案吗?

Jea*_*nan 1020

是的,你可以做到.

在Swift中,您仍然可以使用"#if /#else /#endif"预处理器宏(尽管更受限制),如Apple文档所述.这是一个例子:

#if DEBUG
    let a = 2
#else
    let a = 3
#endif
Run Code Online (Sandbox Code Playgroud)

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

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

我在实际代码中测试它并且它工作; 但它似乎并没有在游乐场中得到认可.

你可以在这里阅读我的原帖.


重要说明: -DDEBUG=1不起作用.只有-D DEBUG作品.似乎编译器忽略了具有特定值的标志.

  • 这是正确的答案,但应该注意的是,您只能检查是否存在标志而不是特定值. (39认同)
  • 在我从这个答案中将格式更改为"-DDEBUG"之前,我无法使用此功能:http://stackoverflow.com/a/24112024/747369. (37认同)
  • **附加注释**:除了如上所述添加`-D DEBUG`之外,还需要在`Apple LLVM 6.0 - Preprocessing` - >`Preprocessor Macros`中定义`DEBUG = 1`. (18认同)
  • @MattQuiros如果您不想在Objective-C代码中使用它,则无需将"DEBUG = 1"添加到"预处理器宏". (11认同)
  • @Daniel您可以使用标准布尔运算符(例如:`#if!DEBUG`) (6认同)
  • @LloydSargent,"#if false"可用于代替"#if 0" (3认同)
  • @Stunner它不是由CocoaPods放置的.我正在使用迦太基,旗帜就在这里.似乎是Xcode 9的默认值. (3认同)
  • 这也适用于自定义文本,不仅**DEBUG** (2认同)
  • 无空间版本(`-DDEBUG`)对我有用,但这个技巧的缺失部分是你需要在*Target*的"Swift编译器 - 自定义标志"部分中添加它,而不是这部分*Project*设置的名称. (2认同)
  • 从 Xcode 9 GM 开始,我不必向构建设置添加任何标志,因为我看到“DEBUG”已经包含在“Swift Compiler-Custom Flags”下的“Active Compilation Conditions”下。但是,我不确定它是默认情况下(当您创建新项目时)还是由 CocoaPods 放置在那里。 (2认同)

And*_*rej 342

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
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

使用Swift 2.2测试

  • 1.还有空白工作,2.应该只为Debug设置标志? (4认同)
  • @ c0ming它取决于你的需求,但如果你想要只在调试模式下发生某些事情而不是发布,你需要从Release中删除-DDEBUG. (3认同)
  • @Andrej你碰巧知道如何使XCTest识别自定义标志吗?我意识到它属于`#if LOCAL`,当我在模拟器中运行并在测试期间落入`#else`时的预期结果.我希望它在测试过程中也会落入`#if LOCAL`. (3认同)
  • 这应该是公认的答案.当前接受的答案对于Swift是不正确的,因为它仅适用于Objective-C. (3认同)

mat*_*att 168

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

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

在此输入图像描述

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

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

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

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

  • **注意**:为所有构建配置设置环境变量,不能为各个构建配置设置它们.因此,如果您需要根据是发布版本还是调试版本来改变行为,那么这不是一个可行的解决方案. (56认同)
  • 环境变量在归档模式下不起作用.它们仅在从XCode启动应用程序时应用.如果您尝试在设备上访问这些内容,该应用程序将崩溃.找到了困难的方法. (9认同)
  • @Eric同意,但他们没有为所有计划行动设置.因此,您可以在构建和运行中执行一项操作,在存档上执行不同的操作,这通常是您想要绘制的真实区别.或者你可以有多种方案,这也是一种现实生活中的共同模式.另外,正如我在回答中所说,在方案中打开和关闭环境变量很容易. (5认同)
  • @ iupchris10“存档没有环境变量”是我上面回答的最后一句话。就像我在回答中说的那样,这是_good_。是_point_。 (2认同)

DSh*_*hah 152

ifdefXcode 8提出了一个重大的替换更改,即使用Active Compilation Conditions.

请参阅建筑和链接Xcode中8注版.

新的构建设置

新设置: SWIFT_ACTIVE_COMPILATION_CONDITIONS

“Active Compilation Conditions” is a new build setting for passing conditional compilation flags to the Swift compiler.
Run Code Online (Sandbox Code Playgroud)

以前,我们必须在OTHER_SWIFT_FLAGS下声明您的条件编译标志,记住在设置前加上"-D".例如,要使用MYFLAG值进行有条件的编译:

#if MYFLAG1
    // stuff 1
#elseif MYFLAG2
    // stuff 2
#else
    // stuff 3
#endif
Run Code Online (Sandbox Code Playgroud)

要添加到设置的值 -DMYFLAG

现在我们只需要将值MYFLAG传递给新设置.是时候移动所有这些条件编译值了!

有关Xcode 8中的更多Swift Build Settings功能,请参阅以下链接:http: //www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-analyzer-improvements/

  • @Jonny 我发现的唯一方法是为项目创建第三个构建配置。从项目 > 信息选项卡 > 配置,点击“+”,然后复制调试。然后,您可以为此配置自定义活动编译条件。不要忘记编辑您的目标 > 测试方案以使用新的构建配置! (2认同)
  • 顺便说一句,在 Xcode 9.3 Swift 4.1 DEBUG 中已经存在于 Active Compilation Conditions 中,您无需添加任何内容来检查 DEBUG 配置。只需#if DEBUG 和#endif。 (2认同)

ken*_*ytm 91

从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()
    }
}
Run Code Online (Sandbox Code Playgroud)

与预处理器宏相比,

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

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

  • 从Swift 3.0和XCode 8开始,这些功能无效. (6认同)
  • 这些功能是否记录在某处? (3认同)
  • 但是,我无法使用这些函数在发布中选择退出某些仅调试变量. (2认同)

Jak*_*lář 84

Xcode 8及以上版本

Build settings/Swift编译器中使用Active Compilation Conditions设置- 自定义标志.

  • 这是用于将条件编译标志传递给Swift编译器的新构建设置.
  • 简单的附加标志是这样的:ALPHA,BETA等等.

然后用这样的编译条件检查它:

#if ALPHA
    //
#elseif BETA
    //
#else
    //
#endif
Run Code Online (Sandbox Code Playgroud)

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


ric*_*ter 75

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

但是,Swift确实包含构建时配置选项,因此您可以有条件地包含某些平台的代码或构建样式,或者响应您使用-D编译器args 定义的标志.但是,与C不同,代码的有条件编译部分必须在语法上完整.在使用Swift With Cocoa和Objective-C时,有一节介绍了这一点.

例如:

#if os(iOS)
    let color = UIColor.redColor()
#else
    let color = NSColor.redColor()
#endif
Run Code Online (Sandbox Code Playgroud)

  • "首先,任意代码替换打破了类型和内存安全性." 预处理器在编译器之前是否完成其工作(因此得名)?因此所有这些检查仍然可以进行. (33认同)
  • @Thilo我认为它破坏的是IDE支持 (10认同)

ing*_*nti 49

我对Xcode 8的两分钱:

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

b)使用更简单:

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

只需添加您的定义WITHOUT -D.


Jon*_*lis 43

isDebug常量基于活动编译条件

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

#if DEBUG
    let isDebug = true
#else
    let isDebug = false
#endif
Run Code Online (Sandbox Code Playgroud)

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
}()
Run Code Online (Sandbox Code Playgroud)

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

  • ✓您无需定义自定义-D DEBUG标志即可使用它
  • 〜它实际上是根据优化设置定义的,而不是Xcode构建配置
  • ✓已记录,这意味着该功能将遵循正常的API发布/弃用模式.

  • ✓在if/else中使用不会生成"永不执行"警告.


Vad*_*ine 22

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

#if DEBUG
#endif
Run Code Online (Sandbox Code Playgroud)

默认情况下,因为在预处理器宏中,DEBUG = 1已由Xcode设置.

所以你可以使用#if DEBUG"开箱即用".

顺便说一下,如何使用条件编译块一般是在Apple的书Swift编程语言4.1(编译器控件语句一节)中编写的,以及如何编写编译标志以及Swift中C宏的对应部分是用另一本Apple的书"将Swift与Cocoa和Objective C一起使用"(在预处理程序指令一节中)

希望将来Apple会为他们的书写下更详细的内容和索引.


Moj*_*ini 15

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

#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 */
Run Code Online (Sandbox Code Playgroud)

此外,您可以使用任何自定义标志,如DEBUG您定义的任何其他标志

#if DEBUG
print("Debug mode")
#endif
Run Code Online (Sandbox Code Playgroud)


mid*_*n p 13

XCODE 9及以上

#if DEVELOP
    //
#elseif PRODCTN
    //
#else
    //
#endif
Run Code Online (Sandbox Code Playgroud)


Saz*_*han 12

Moignans 在这里的回答很好。如果有帮助,这里还有另一条信息,

#if DEBUG
    let a = 2
#else
    let a = 3
#endif
Run Code Online (Sandbox Code Playgroud)

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

#if !RELEASE
    let a = 2
#else
    let a = 3
#endif
Run Code Online (Sandbox Code Playgroud)


Riv*_*era 7

设置完成后DEBUG=1您的GCC_PREPROCESSOR_DEFINITIONS生成设置我更喜欢使用的功能,使这个调用:

func executeInProduction(_ block: () -> Void)
{
    #if !DEBUG
        block()
    #endif
}
Run Code Online (Sandbox Code Playgroud)

然后在这个函数中包含我想在Debug构建中省略的任何块:

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

与以下方面相比的优势:

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

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