Swift语言中的#ifdef替代方法

875

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

#ifdef DEBUG
    // Debug-only code
#endif

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


1
作为一个想法,你可以将这个放在你的 Obj-C 桥接头文件中。 - Matej
75
由于您有多个可供选择的答案,而且这个问题已经得到了很多赞,所以您真的应该选出一个最佳答案。 - David H
1
@Userthatisnotauser,你完全没有理解重点。你提问,你得到了很好的答案——选择一个。不要忽视时间和精力。 - David H
1
@DavidH 不,实际上是相反的。我的评论只是关于42的《银河系漫游指南》的参考。我完全同意,并想点赞,但我无法让自己成为第43个点赞者。 - ReinstateMonica3167040
2
@Userthatisnotauser 这位发帖者有19k积分 - 人们投票支持他的回答,但他似乎不关心那些帮助他的人。我总是会选择一个答案。 - David H
1
检查他们的账户,它已经死了。 - user2875404
19个回答

29

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

#if DEBUG
#endif

由于在预处理器宏 DEBUG=1 已经被 Xcode 设置,所以默认情况下可正常工作。

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

顺便提一句,如何使用条件编译块通常在苹果的书籍《The Swift Programming Language 4.1》(部分:编译器控制语句)中有介绍,而如何编写编译标志以及Swift中C宏的对应项在另一本苹果的书籍《Using Swift with Cocoa and Objective C》(部分:预处理指令)中有介绍。

希望苹果在未来能编写更详细的内容和索引。


25

XCODE 9 及以上版本

#if DEVELOP
    //print("Develop")
#elseif PRODUCTION
    //print("Production")
#else
    //
#endif

24

有一些处理器需要一个参数,我列在下面。你可以随意更改该参数:

#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

8
在您的构建设置中设置GCC_PREPROCESSOR_DEFINITIONSDEBUG=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

编译器会检查我的代码语法,以确保它的语法正确并能够构建。


5

1
你在这里找到了答案!Swift #if 指令查看自定义标志而非预处理宏。请使用链接中的内容更新你的回答,因为链接经常会在一段时间后失效。 - Dale

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

Source


1
这不是条件编译。虽然有用,但它只是一个普通的运行时条件语句。原帖作者询问的是关于编译期元编程的。 - Shayne
3
只需在func前面添加@inlinable,这将成为Swift中最优雅和惯用的方式。在发布版本中,您的code()块将被优化并完全消除。苹果自己的NIO框架中使用了类似的函数。 - mojuba

2

这是基于 Jon Willis 的答案构建的,它依赖于 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完全消除了函数调用。

在非调试模式下完全消除调用 - 因为空函数?那就太棒了。 - Johan

0

Swift 5 版本更新,详见马特的回答

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

0
您可以创建一个名为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")

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接