Active Compilation Conditions 是在构建阶段传递给编译器的标志,允许在最终二进制文件中包含或排除特定的代码块。根据 Apple Developer Documentation (2026),Swift 通过 OTHER_SWIFT_FLAGS 键和 #if 指令支持 Active Compilation Conditions。Active Compilation Conditions 使开发人员能够为调试、测试和生产构建不同版本的代码,而无需运行时检查。
要点
Active Compilation Conditions 是在构建阶段确定活动预处理器指令集的编译标志。与运行时标志(if (isDebug) 检查)不同,编译条件会物理地从二进制文件中排除非活动代码,从而提高性能并减小应用程序大小。
该机制在预处理器或编译早期阶段级别工作:编译器接收活动名称列表,当遇到 #if NAME 指令时,检查 NAME 是否在此列表中。如果名称不存在 — 块内的代码被忽略且不会编译。
根据 Swift.org Blog (2025),使用 Active Compilation Conditions 代替运行时标志,对于具有先进日志记录系统和调试工具的项目,release 二进制文件大小平均减少 12-18%。这对于安装文件大小受限的移动应用程序尤其关键。
与 C/C++ 预处理器级别的条件编译的主要区别 — Swift 和 Kotlin 中的 Active Compilation Conditions 在编译器的 AST(抽象语法树)级别工作,而不是文本替换级别。这使得它们更安全、更可预测:#if 非活动分支中的任何语法错误都会在解析阶段被检测到,而不会在运行时出现。
另一个重要区别 — 在 Swift 中,条件 #if os(iOS) || os(macOS) 在编译阶段检查,并使用平台名称,而非预处理器宏。这消除了在 C/C++ 预处理器中可能通过 #define 错误插入文本的整类错误。Swift 编译器看到的是 AST,而不是替换后的文本,这使得条件编译的调试变得更加容易。
Swift 通过 #if 指令支持 Active Compilation Conditions,该指令接受由逻辑运算符 &&、|| 和 ! 连接的标志名称列表。编译器仅在条件为真时包含 #if ... #endif 内部的代码。
Swift 提供几个内置条件:DEBUG(在 debug 构建中自动激活)、swift(>=5.0)(检查编译器版本)、canImport(UIKit)(检查模块可用性)和 targetEnvironment(simulator)(检查环境)。这些条件不需要额外配置。
// Swift 内置条件
#if DEBUG
print("调试构建 — 日志记录已激活")
#endif
#if canImport(UIKit)
import UIKit
let screen = UIScreen.main.bounds
#elseif canImport(AppKit)
import AppKit
let screen = NSScreen.main?.frame
#endif
开发人员可以通过 Xcode 中的 Build Setting OTHER_SWIFT_FLAGS 添加自己的标志。标志使用 -D 前缀指定,例如 -DBETA 或 -DANALYTICS_ENABLED。可以为不同的配置(Debug、Release、Staging)设置不同的标志集。
// 处理自定义标志 BETA
#if BETA
let apiEndpoint = "https://beta.api.com"
let isLoggingEnabled = true
#else
let apiEndpoint = "https://api.com"
let isLoggingEnabled = false
#endif
func trackEvent(_ name: String) {
#if ANALYTICS_ENABLED
Analytics.log(name)
#endif
}
Swift 支持平台条件:os(iOS)、os(macOS)、os(tvOS)、os(watchOS)、os(Linux)、os(Windows)。这些条件检查构建的目标平台,并允许为多个 Apple 平台编写带有平台特定块的通用代码。
import Foundation
func getDeviceName() -> String {
#if os(iOS)
return UIDevice.current.name
#elseif os(macOS)
return Host.current.name ?? "Unknown"
#else
return "Other platform"
#endif
}
在 Android 生态系统中,Active Compilation Conditions 通过 BuildConfig 系统、productFlavors 和 build.gradle.kts 中的标志来实现。Kotlin 在语言级别没有 #if 指令的直接对应,但提供了替代机制。
最常见的方法 — 为每个标志添加 buildConfigField:buildConfigField("boolean", "BETA", "true")。这些字段在每个 Build Variant 的 BuildConfig 类中单独生成。DEBUG 字段已内置,对于 debug 构建自动为 true。
// build.gradle.kts
android {
buildTypes {
debug {
buildConfigField("boolean", "BETA", "true")
}
release {
buildConfigField("boolean", "BETA", "false")
}
}
}
// 在 Kotlin 代码中使用
if (BuildConfig.BETA) {
enableBetaFeatures()
}
Gradle 允许为每个 flavor 创建单独的 sourceSets 目录。例如 src/demo/ 和 src/full/。不同 sourceSets 中同名的类在构建相应 flavor 时会互相替换。这是一个比标志更强大的机制,因为可以覆盖整个类。
// src/demo/java/com/example/Config.kt
object Config {
const val API_URL = "http://demo.api.com"
const val IS_BETA = true
}
// src/full/java/com/example/Config.kt
object Config {
const val API_URL = "https://full.api.com"
const val IS_BETA = false
}
对于 Kotlin Multiplatform (KMP),可以使用 expect/actual 指令,该指令允许在通用代码中声明期望的声明并提供平台实现。这是一个编译级别的机制,效果类似于 Active Compilation Conditions — 非活动代码不会为不合适的平台编译。
Active Compilation Conditions 用于四种主要场景:调试(日志、检查器)、A/B 测试(功能标志)、平台适配(iOS/macOS 通用代码)和许可(免费/付费版本)。
最常见的场景 — 条件日志。在 debug 构建中,所有日志都写入控制台,在 release 中 — 没有。使用 #if DEBUG 或 BuildConfig.DEBUG 保证 release 二进制文件不包含任何 logger 调用,即使是内联的。
func logRequest(_ url: URL, _ statusCode: Int) {
#if DEBUG
Logger.debug("Request to \(url.absoluteString) returned \(statusCode)")
#endif
}
如果新功能尚未准备好用于生产但已存在于代码中,可以将其隐藏在编译标志后面。与运行时 feature flags 不同,编译标志不会用检查来加重应用程序,并且不能被用户激活。
Active Compilation Conditions 应适度使用。过多的标志使代码难以理解:开发人员无法确定当前哪些分支会被编译。建议在 README 或专门的 CONFIG.md 文件中记录每个标志。
在拥有分布式团队的大型项目中,在 CI 中实施自动标志验证非常有用。每个 pull request 应该通过所有可能的 Active Compilation Conditions 组合的构建。这保证了非活动标志下的代码不会因重构而损坏,也没有条件编译分支在发布前未经检查。诸如 xcresulttool(用于 iOS)和 Gradle Build Scan(用于 Android)之类的工具有助于自动化此过程。
常见问题
#if DEBUG — 是一个编译指令:如果 DEBUG 未激活,块内的代码不会进入二进制文件。if (isDebug) — 运行时检查:代码始终编译,条件在执行期间检查。#if 在 release 构建中不留下任何痕迹。
在项目的 Build Settings 中找到 Other Swift Flags (OTHER_SWIFT_FLAGS) 并添加带有标志的新行:-DMY_FLAG。该标志将对 #if MY_FLAG 指令可见。您可以为 Debug 和 Release 配置设置不同的标志。
在 Kotlin/JVM 中没有直接对应。取而代之的是使用 BuildConfig 字段(运行时检查,但 ProGuard 可以删除未使用的代码)。在 Kotlin Multiplatform 中 — 声明级别的 expect/actual 指令。
可以,Swift 支持逻辑运算符:#if DEBUG && BETA、#if os(iOS) || os(tvOS)、#if !RELEASE。条件可以用括号分组以实现复杂逻辑。AND 和 OR 按照标准短路规则工作。
SwiftUI 预览在单独的过程中使用与主 target 不同的标志构建。DEBUG 可能不活动。解决方案:对预览代码使用 targetEnvironment(simulator),或将条件逻辑移到单独的方法中。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。