在C/C++/Objective C中,您可以使用编译器预处理器定义宏。 此外,您还可以使用编译器预处理器包含/排除一些代码部分。
#ifdef DEBUG
// Debug-only code
#endif
在 Swift 中是否有类似的解决方案?
在C/C++/Objective C中,您可以使用编译器预处理器定义宏。 此外,您还可以使用编译器预处理器包含/排除一些代码部分。
#ifdef DEBUG
// Debug-only code
#endif
在 Swift 中是否有类似的解决方案?
是的,你可以做到。
在Swift中,你仍然可以使用"#if/#else/#endif"预处理器宏(尽管更受限制),正如苹果文档所述。这里是一个例子:
#if DEBUG
let a = 2
#else
let a = 3
#endif
现在,你必须在其他地方设置“DEBUG”符号。在“Swift编译器 - 自定义标志”部分中,设置“其他Swift标志”行。您可以使用-D DEBUG
条目添加DEBUG符号。
通常情况下,在Debug或Release模式下可以设置不同的值。
我在真实代码中测试过它,它可以工作;但在playground中似乎无法识别。
您可以在这里阅读我的原始帖子。
重要提示:-DDEBUG=1
不起作用。只有-D DEBUG
起作用。似乎编译器忽略了具有特定值的标志。
-D DEBUG
之外,你还需要在“Apple LLVM 6.0 - 预处理”->“预处理器宏”中定义DEBUG=1
。请注意不要改变原来的意思,并尽量使翻译通俗易懂。 - Matthew QuirosDEBUG=1
添加到Preprocessor Macros
中。 - derpoliuk #if !DEBUG
)。 - Jean Le MoignanSwift编译器不包括预处理器。相反,它利用编译时属性、构建配置和语言特性来完成相同的功能。因此,在Swift中不会导入预处理器指令。
我通过使用自定义Build Configurations实现了我想要的功能:
以下是检查目标的方法:
#if BANANA
print("We have a banana")
#elseif MELONA
print("Melona")
#else
print("Kiwi")
#endif
使用Swift 2.2进行测试
#if LOCAL #else #endif
中,它会落入#else
部分。我复制了原始目标AppTarget
并将其重命名为AppTargetLocal
,并设置了其自定义标志。 - Perwyl Liu#if LOCAL
中,当我在模拟器上运行时,而在测试期间则会落入#else
中。我希望在测试期间它也能落入#if LOCAL
中。 - Perwyl Liu在许多情况下,您实际上不需要条件编译;您只需要可以开关的条件行为。为此,您可以使用环境变量。这具有巨大优势,即您实际上不必重新编译。
您可以在方案编辑器中设置环境变量,并轻松切换它:
您可以使用NSProcessInfo检索环境变量:
let dic = NSProcessInfo.processInfo().environment
if dic["TRIPLE"] != nil {
// ... do secret stuff here ...
}
这里有一个真实的例子。我的应用程序只能在设备上运行,因为它使用音乐库,而模拟器上没有音乐库。那么,如何在我不拥有的设备上使用模拟器进行截屏呢?没有这些截屏,我就无法提交到AppStore。
我需要假数据和不同的处理方式。我有两个环境变量:其中一个在开启时告诉应用程序在我的设备上运行时从真实数据生成假数据;另一个在开启时在模拟器上运行时使用假数据(而非缺失的音乐库)。每次切换这些特殊模式的开/关状态都很容易,因为在Scheme编辑器中有环境变量复选框。而且好处是,我无法在我的App Store构建中意外地使用它们,因为归档没有环境变量。
Xcode 8带来了关于ifdef
替换的重大变化,即使用Active Compilation Conditions。
请参考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/
从Swift 4.1开始,如果你只需要检查代码是使用调试还是发布配置构建,你可以使用内置函数:
_isDebugAssertConfiguration()
(当优化设置为-Onone
时返回true)_isReleaseAssertConfiguration()
(当优化设置为-O
时返回true)_isFastAssertConfiguration()
(当优化设置为-Ounchecked
时返回true)例如:
func obtain() -> AbstractThing {
if _isDebugAssertConfiguration() {
return DecoratedThingWithDebugInformation(Thing())
} else {
return Thing()
}
}
与预处理器宏相比,
使用构建设置 / Swift编译器 - 自定义标志中的活动编译条件设置。
ALPHA
,BETA
等。然后像这样检查它:编译条件。
#if ALPHA
//
#elseif BETA
//
#else
//
#endif
提示:你也可以使用
#if !ALPHA
等。
Swift 没有预处理器。(首先,任意代码替换会破坏类型和内存安全性。)
不过,Swift 包括构建时配置选项,因此您可以根据特定平台或构建样式或响应您使用 -D
编译器参数定义的标志来有条件地包含代码。与 C 不同的是,您代码的有条件编译部分必须在语法上完整。关于这个问题,在《Using Swift With Cocoa and Objective-C》中有一节讨论。
例如:
#if os(iOS)
let color = UIColor.redColor()
#else
let color = NSColor.redColor()
#endif
INT_CONST
放在任何接受float
的地方。Swift不允许这样做。此外,如果您可以执行var floatVal = INT_CONST
,那么当编译器期望一个Int
但您将其用作Float
(floatVal
的类型将被推断为Int
)时,它最终会在某个地方崩溃。经过10次强制转换后,删除宏只是更清晰的选择... - Ephemera另一个也许更简单的解决方案,仍然可以得到一个布尔值,您可以将其传递到函数中,而不需要在代码库中遍布#if
条件语句,那就是将 DEBUG
定义为您项目构建目标的Active Compilation Conditions
之一,并包含以下内容(我将其定义为全局常量):
#if DEBUG
let isDebug = true
#else
let isDebug = false
#endif
这个概念是在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
标志来使用它✓ 已记录文档,这意味着该函数将遵循常规的API发布/废弃模式。
✓ 在if / else中使用不会生成“永远不会被执行”的警告。
Xcode 8个人建议:
a) 使用-D
前缀的自定义标志可以正常工作,但是...
b) 更简单的用法:
Xcode 8中有一个新的部分:“活动编译条件”,已经有两行,用于调试和发布。
只需添加您的定义,而不需要使用-D
。
Moignans 这里的答案 可以正常工作。这里提供另外一些信息,以帮助理解。
#if DEBUG
let a = 2
#else
let a = 3
#endif
您可以像以下这样否定宏:
#if !RELEASE
let a = 2
#else
let a = 3
#endif