首页 文章

用Swift语言替换#ifdef

提问于
浏览
611

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

#ifdef DEBUG
    // Debug-only code
#endif

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

15 回答

  • 2

    我对Xcode 8的两分钱:

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

    b)使用更简单:

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

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

  • 1

    GCC_PREPROCESSOR_DEFINITIONS Build Settings中设置 DEBUG=1 后,我更喜欢使用函数来进行此调用:

    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
    

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

  • 900

    Apple Docs所述

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

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

    • 转到您的项目/选择目标/构建设置/搜索自定义标志

    • 对于您选择的目标,使用-D前缀(不带空格)为Debug和Release设置自定义标志

    • 为您拥有的每个目标执行上述步骤

    以下是检查目标的方法:

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

    使用Swift 2.2测试

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

    Source

  • 33

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

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

    enter image description here

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

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

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

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

  • 151

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

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

    例如:

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

    这构建在依赖于assert的Jon Willis's answer上,而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完全消除了函数调用 .

  • 77

    Xcode 8出现了 ifdef 替换的一个重大变化,即使用 Active Compilation Conditions .

    请参阅Xcode 8 Release note中的 Building and Linking .

    新的构建设置

    新设置: 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/

  • 301

    Xcode 8及以上

    Build settings / Swift compiler - Custom flags 中使用 Active Compilation Conditions 设置 .

    • 这是用于将条件编译标志传递给Swift编译器的新构建设置 .

    • 简单添加这样的标志: ALPHABETA 等 .

    然后用 compilation conditions 检查它,如下所示:

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

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

  • 75

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

    #if DEBUG
    #endif
    

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

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

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

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

  • 43

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

    • _isDebugAssertConfiguration() (优化设置为 -Onone 时为true)

    • _isReleaseAssertConfiguration()(当优化设置为-O时为true)(在Swift 3上不可用)

    • _isFastAssertConfiguration() (优化设置为 -Ounchecked 时为true)

    例如

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

    与预处理器宏相比,

    • ✓您无需定义自定义 -D DEBUG 标志即可使用它

    • ~它实际上是根据优化设置定义的,而不是Xcode构建配置

    • ✗未记载,这意味着可以在任何更新中删除该函数(但它应该是AppStore安全的,因为优化器会将这些变为常量)

    • 这些曾经removed,但brought back to public to lack of @testable attribute,未来斯威夫特的命运不确定 .

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

  • 2

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

    在代码中

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

    是的,你可以做到 .

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

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

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

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

    我用真实的代码测试它,它的工作原理;但它似乎并没有在游乐场中得到认可 .

    你可以阅读我原来的帖子here .


    IMPORTANT NOTE: -DDEBUG=1 不起作用 . 只有 -D DEBUG 有效 . 似乎编译器忽略了具有特定值的标志 .

  • 2

    isDebug常量基于活动编译条件

    另一个也许更简单的解决方案仍然会产生一个布尔值,您可以将代码库中的 #if 条件定义为 DEBUG ,并将其定义为 DEBUG 并将其定义为全局常量 . :

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

    isDebug常量基于编译器优化设置

    这个概念 Build 于kennytm's answer

    与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构建配置

    • Documented ,这意味着该函数将遵循正常的API发布/弃用模式 .

    • ✓在if / else中使用将 not 生成"Will never be executed"警告 .

  • 70

    XCODE 9 AND ABOVE

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

相关问题