在C/C++/Objective C中,可以使用编译器预处理器定义宏。此外,您可以使用编译器预处理器包含/排除代码的某些部分。
#ifdef DEBUG
// Debug-only code
#endif
Swift中有类似的解决方案吗?
在C/C++/Objective C中,可以使用编译器预处理器定义宏。此外,您可以使用编译器预处理器包含/排除代码的某些部分。
#ifdef DEBUG
// Debug-only code
#endif
Swift中有类似的解决方案吗?
没有Swift预处理器。(一方面,任意代码替换破坏了类型和内存安全。)
不过,Swift确实包含了构建时配置选项,因此您可以有条件地包含某些平台或构建样式的代码,或者响应您使用-D编译器参数定义的标志。但是,与C不同,代码的有条件编译部分必须在语法上完整。在将Swift与Cocoa和Objective-C结合使用中有一节介绍了这一点。
例如:
#if os(iOS)
let color = UIColor.redColor()
#else
let color = NSColor.redColor()
#endif
是的,你能做到。
根据Apple文档,在Swift中,您仍然可以使用“#if/#else/#endif”预处理器宏(尽管更受限制)。下面是一个示例:
#if DEBUG
let a = 2
#else
let a = 3
#endif
现在,您必须在其他地方设置“调试”符号。在“Swift编译器-自定义标志”部分的“其他Swift标志”行中设置它。您可以使用-D DEBUG条目添加DEBUG符号。
通常,您可以在“调试”或“发布”中设置不同的值。
我在真实代码中测试了它,它工作了;不过,在操场上似乎认不出它。
你可以在这里阅读我的原始帖子。
重要提示:-DDEBUG=1不起作用。只有-D DEBUG有效。编译器似乎忽略了具有特定值的标志。
在许多情况下,您实际上不需要条件编译;您只需要可以打开和关闭的条件行为。为此,您可以使用环境变量。这有一个巨大的优点,你实际上不需要重新编译。
您可以在方案编辑器中设置环境变量,并轻松地将其打开或关闭:
您可以使用NSProcessInfo检索环境变量:
let dic = NSProcessInfo.processInfo().environment
if dic["TRIPLE"] != nil {
// ... do secret stuff here ...
}
这是一个现实生活中的例子。我的应用程序只在设备上运行,因为它使用的是模拟器上不存在的音乐库。那么,如何在模拟器上拍摄我不拥有的设备的屏幕快照?没有这些屏幕截图,我无法提交到AppStore。
我需要假数据和不同的处理方式。我有两个环境变量:一个是当打开时,告诉应用程序在我的设备上运行时从真实数据生成假数据;另一个,当打开时,在模拟器上运行时使用假数据(而不是丢失的音乐库)。由于Scheme编辑器中的环境变量复选框,打开/关闭这些特殊模式很容易。另外一个好处是,我不会在我的App Store构建中意外使用它们,因为归档没有环境变量。
从Swift 4.1开始,如果你只需要检查代码是用调试还是发布配置构建的,你可以使用内置函数:
_isDebugAssertConfiguration()(当优化设置为-Onone时为true)_isReleaseAssertConfiguration()(当优化设置为-O时为true)(在Swift 3+上不可用)_isFastAssertConfiguration()(当优化设置为-Ounchecked时为true)
e.g.
func obtain() -> AbstractThing {
if _isDebugAssertConfiguration() {
return DecoratedThingWithDebugInformation(Thing())
} else {
return Thing()
}
}
与预处理器宏相比,
✓ 您不需要定义自定义的-D DEBUG标志来使用它~它实际上是根据优化设置定义的,而不是Xcode构建配置✗ 未记录,这意味着该函数可以在任何更新中删除(但它应该是AppStore安全的,因为优化器会将这些转换为常量)这些内容一度被删除,但由于缺少@testable属性而重新公开,未来的Swift命运未卜。✗ 在if/else中使用将始终生成“将永远不会执行”警告。
如Apple Docs中所述
Swift编译器不包含预处理器。相反,它利用编译时属性、构建配置和语言特性来实现相同的功能。因此,在Swift中不导入预处理器指令。
我已经通过使用自定义构建配置实现了我想要的目标:
转到项目/选择目标/构建设置/搜索自定义标志对于所选目标,使用-D前缀(不带空格)为调试和发布设置自定义标志为您的每个目标执行以上步骤
以下是检查目标的方法:
#if BANANA
print("We have a banana")
#elseif MELONA
print("Melona")
#else
print("Kiwi")
#endif
使用Swift 2.2测试
我的两分钱用于Xcode 8:
a) 使用-D前缀的自定义标志工作正常,但。。。
b) 更简单的使用:
在Xcode 8中有一个新的部分:“活动编译条件”,已经有两行,用于调试和发布。
只需添加无D的定义。
Xcode 8对ifdef替换进行了重大更改。即使用主动编译条件。
请参阅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传递到新设置。是时候移动所有这些条件编译值了!
有关Xcode 8中更多Swift Build Settings功能,请参阅以下链接:http://www.miqu.me/blog/2016/07/31/xcode-8-new-build-settings-and-analyzer-improvements/
Xcode 8及以上
使用Build设置/Swift编译器-自定义标志中的Active Compilation Conditions设置。
这是将条件编译标志传递给Swift编译器的新构建设置。简单地添加如下标志:ALPHA、BETA等。
然后使用如下编译条件进行检查:
#if ALPHA
//
#elseif BETA
//
#else
//
#endif
提示:您也可以使用#if!ALPHA等。
isDebug常量基于活动编译条件
另一个可能更简单的解决方案是,将DEBUG定义为项目构建目标的Active Compilation Conditions之一,并包含以下内容(我将其定义为全局常量):
#if DEBUG
let isDebug = true
#else
let isDebug = false
#endif
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
}()
与预处理器宏和kennytm的答案相比,
✓ 您不需要定义自定义的-D DEBUG标志来使用它~它实际上是根据优化设置定义的,而不是Xcode构建配置✓ 已记录,这意味着该函数将遵循正常的API发布/弃用模式。✓ 在if/else中使用不会生成“将永远不会执行”警告。
这是基于Jon Willis的答案,该答案依赖于断言,该断言仅在调试编译中执行:
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完全消除了函数调用。
在GCC_PREPROCESSOR_DEFINITIONS构建设置中设置DEBUG=1后,我更喜欢使用函数进行以下调用:
func executeInProduction(_ block: () -> Void)
{
#if !DEBUG
block()
#endif
}
然后,只需在此函数中包含我希望在调试构建中省略的任何块:
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
是编译器检查我的代码的语法,所以我确信它的语法是正确的,并进行编译。
![在Xcode 8及以上版本中,转到构建设置->搜索自定义标志]1
在代码中
#if Live
print("Live")
#else
print("debug")
#endif
在使用Xcode版本9.4.1创建的Swift项目中,Swift 4.1
#if DEBUG
#endif
默认情况下工作,因为在预处理器宏中,Xcode已设置DEBUG=1。
因此,您可以使用#if DEBUG“开箱即用”。
顺便说一句,苹果的《Swift编程语言4.1》一书(编译器控制语句一节)中写了如何使用条件编译块,而如何编写编译标志以及Swift中C宏的对应内容,则在另一本苹果的《将Swift与Cocoa和Objective C结合使用》一书中(预处理器指令一节)
希望未来苹果会为他们的书写更详细的内容和索引。
XCODE 9及以上
#if DEVELOP
//print("Develop")
#elseif PRODUCTION
//print("Production")
#else
//
#endif
莫伊尼昂的回答很好。这是另一条信息,以防有帮助,
#if DEBUG
let a = 2
#else
let a = 3
#endif
您可以像下面这样否定宏,
#if !RELEASE
let a = 2
#else
let a = 3
#endif
有一些处理器接受一个参数,我在下面列出了它们。您可以随意更改参数:
#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
马特回答的Swift 5更新
let dic = ProcessInfo.processInfo.environment
if dic["TRIPLE"] != nil {
// ... do your secret stuff here ...
}