在C / C ++ / Objective-C中,您可以使用编译器预处理器定义宏。 此外,您可以使用编译器预处理器包含/排除代码的某些部分。
#ifdef DEBUG
// Debug-only code
#endif
Swift中是否有类似的解决方案?
答案 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)
Swift编译器不包含预处理器。相反,它利用编译时属性,构建配置和语言功能来实现相同的功能。因此,预处理程序指令不会在Swift中导入。
我已经设法通过使用自定义构建配置实现了我想要的目标:
答案 2 :(得分:160)
在许多情况下,您并不需要有条件的编译;您只需要可以打开和关闭的条件行为。为此,您可以使用环境变量。这具有巨大的优势,您实际上不必重新编译。
您可以在方案编辑器中设置环境变量,并轻松打开或关闭它:
您可以使用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)_isFastAssertConfiguration()
(当优化设置为-Ounchecked
时为真)e.g。
func obtain() -> AbstractThing {
if _isDebugAssertConfiguration() {
return DecoratedThingWithDebugInformation(Thing())
} else {
return Thing()
}
}
与预处理器宏相比,
-D DEBUG
标志即可使用✗未记载,这意味着可以在任何更新中删除该函数(但它应该是AppStore安全的,因为优化器会将这些变为常量)
@testable
attribute,未来Swift的命运不确定。✗在if / else中使用将始终生成“永不执行”警告。
答案 5 :(得分:79)
在构建设置/ Swift编译器 - 自定义标记中使用有效编译条件设置。
ALPHA
,BETA
等。然后使用 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)
另一个也许更简单的解决方案仍然会产生一个布尔值,你可以在整个代码库中传递给函数而不需要#if
个条件,就是将DEBUG
定义为你的项目构建目标之一{{1并包含以下内容(我将其定义为全局常量):
Active Compilation Conditions
这个概念建立在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
}()
标志即可使用✓已记录,表示该功能将遵循正常的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 }())
}