C/C++/Objective-Cでは、コンパイラプリプロセッサを使ってマクロを定義できます。さらに、コンパイラプリプロセッサを使用してコードの一部を含める/除外することができます。
#ifdef DEBUG
// Debug-only code
#endif
Swiftでも同様の解決策はありますか?
はい、できます。
Swiftでも、 Apple docs のように、 "#if /#else /#endif"プリプロセッサマクロを使うことができます(より制限はありますが)。これが例です:
#if DEBUG
let a = 2
#else
let a = 3
#endif
しかし今は、他の場所に "DEBUG"シンボルを設定しなければなりません。 "Swift Compiler - Custom Flags"セクションの "Other Swift Flags"行に設定してください。 DEBUGシンボルを-D DEBUG
エントリで追加します。
通常どおり、デバッグ時またはリリース時に異なる値を設定できます。
私はそれを実際のコードでテストしましたが、うまくいきました。遊び場では認識されていないようです。
私の元の記事を読むことができます ここ 。
重要な注意: -DDEBUG=1
は動作しません。 -D DEBUG
だけが動作します。コンパイラが特定の値を持つフラグを無視しているようです。
Apple Docs に記載されているように
Swiftコンパイラはプリプロセッサを含みません。代わりに、コンパイル時の属性、ビルド構成、および言語機能を利用して同じ機能を実現します。このため、Swiftではプリプロセッサディレクティブはインポートされません。
私は、カスタムビルド構成を使用して、自分が望んでいたことを達成できました。
ターゲットを確認する方法は次のとおりです。
#if BANANA
print("We have a banana")
#elseif MELONA
print("Melona")
#else
print("Kiwi")
#endif
Swift 2.2を使用してテスト済み
多くの場合、条件付き コンパイル は必要ありません。あなただけの条件付き 動作 あなたがオンとオフを切り替えることができます必要があります。そのためには、環境変数を使うことができます。これには、実際に再コンパイルする必要がないという大きな利点があります。
スキームエディタで環境変数を設定し、簡単にオンまたはオフに切り替えることができます。
NSProcessInfoを使って環境変数を取得できます。
let dic = NSProcessInfo.processInfo().environment
if dic["TRIPLE"] != nil {
// ... do secret stuff here ...
}
これが現実の例です。私のアプリは、Simulatorには存在しないミュージックライブラリを使用しているため、デバイス上でのみ動作します。それでは、どのようにして私が所有していないデバイスのシミュレータでスクリーンショットを撮るのでしょうか。スクリーンショットがないと、AppStoreに送信できません。
偽のデータ と それを処理する別の方法 が必要です。私は2つの環境変数を持っています。1つは、スイッチを入れると、私のデバイスで実行中に実際のデータから偽のデータを生成するようにアプリに指示します。もう1つは、シミュレータ上で実行されている間、電源を入れたときに、(見つからない音楽ライブラリではなく)偽のデータを使用します。 Schemeエディタの環境変数チェックボックスのおかげで、これらの特別モードのオン/オフを切り替えるのは簡単です。そして、アーカイブには環境変数がないため、App Storeのビルドで誤ってそれらを使用することはできません。
ifdef
の置き換えの大きな変更はXcode 8で起こりました。すなわち、 アクティブなコンパイル条件 の使用です。
Xcode 8リリースノート の 構築とリンク を参照してください。
新しいビルド設定
新しい設定:Swift_ACTIVE_COMPILATION_CONDITIONS
“Active Compilation Conditions” is a new build setting for passing conditional compilation flags to the Swift compiler.
以前は、設定の前に「-D」を付けることを忘れずに、OTHER_Swift_FLAGSの下に条件付きコンパイルフラグを宣言する必要がありました。たとえば、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-改善点/
Swift 4.1では、コードがデバッグまたはリリース構成でビルドされているかどうかを確認するだけでよい場合は、組み込み関数を使用できます。
_isDebugAssertConfiguration()
(最適化が-Onone
に設定されている場合はtrue)_isReleaseAssertConfiguration()
(最適化が-O
に設定されている場合はtrue)_isFastAssertConfiguration()
(最適化が-Ounchecked
に設定されている場合はtrue)例えば.
func obtain() -> AbstractThing {
if _isDebugAssertConfiguration() {
return DecoratedThingWithDebugInformation(Thing())
} else {
return Thing()
}
}
プリプロセッサマクロと比較して、
-D DEBUG
フラグを定義する必要はありません。文書化されていません。つまり、関数は更新のたびに削除できます(ただし、オプティマイザはこれらを定数に変換するため、AppStoreセーフである必要があります)。
@testable
属性の欠如により一般に持ち帰られました 、将来のSwiftでは不確定です。if if/elseで使用すると、常に "実行されない"という警告が表示されます。
Build settings/Swiftコンパイラ - カスタムフラグのActive Compilation Conditions設定を使用してください。
ALPHA
、BETA
などのように単純にフラグを追加します。それからコンパイル条件のようにチェックしてください。
#if ALPHA
//
#elseif BETA
//
#else
//
#endif
ヒント:
#if !ALPHA
なども使用できます
Swiftプリプロセッサはありません。 (1つには、任意のコードを代入すると、型およびメモリの安全性が損なわれます。)
ただし、Swiftにはビルド時の設定オプションが含まれているため、特定のプラットフォームやビルドスタイル、または-D
コンパイラ引数で定義したフラグに応じて、条件付きでコードを含めることができます。ただしCとは異なり、コードの条件付きコンパイル済みセクションは構文的に完全でなければなりません。これについてのセクションは CocoaとObjective-CでのSwiftの使用 にあります。
例えば:
#if os(iOS)
let color = UIColor.redColor()
#else
let color = NSColor.redColor()
#endif
Xcode 8の私の2セント:
a)-D
接頭辞を使ったカスタムフラグはうまく働きますが、...
b)より簡単に使う:
Xcode 8には、デバッグとリリースのために、すでに2行の「アクティブコンパイル条件」という新しいセクションがあります。
-D
を付けずにdefineを追加するだけです。
コードベース全体で#if
条件式をこじ開けずに関数に渡すことができるブール値になる可能性があるもう1つの、おそらくもっと簡単な解決策は、DEBUG
をプロジェクトビルドターゲットのActive Compilation Conditions
の1つとして定義し、次を含めることです:
#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
フラグを定義する必要はありません。✓Documented、これは関数が通常のAPIリリース/非推奨パターンに従うことを意味します。
✓if/elseでnotを使用すると、「実行されません」という警告が生成されます。
Xcodeバージョン9.4.1、Swift 4.1で作成されたSwiftプロジェクト
#if DEBUG
#endif
プリプロセッサマクロではDEBUG = 1がすでにXcodeによって設定されているので、デフォルトで動作します。
そのため、#if DEBUG "out of box"を使用できます。
ちなみに、条件コンパイルブロックの一般的な使い方は、Appleの著書 『The Swift Programming Language 4.1』(セクションCompiler Control Statements)に書かれています。コンパイルフラグの書き方と、SwiftのCマクロに相当するものは、別のAppleの本「CocoaとObjective CでのSwiftの使い方」(プリプロセッサディレクティブのセクションにあります)
将来的にはAppleが彼らの本のためにより詳細な内容と索引を書くことを願っています。
Xコード9以上
#if DEVELOP
//
#elseif PRODCTN
//
#else
//
#endif
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
コンパイラは私のコードの構文をチェックするので、その構文が正しいことを確認してビルドします。
これは Jon Willisの answerに基づいており、これはデバッグコンパイルでのみ実行されます。
func Log(_ str: String) {
assert(DebugLog(str))
}
func DebugLog(_ str: String) -> Bool {
print(str)
return true
}
私のユースケースはprintステートメントをログに記録することです。これが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は関数呼び出しを完全に排除しているように見えます。
func inDebugBuilds(_ code: () -> Void) {
assert({ code(); return true }())
}
Moignans 答え ここでうまく動作します。これが役立つ場合のための情報のもう一つの平和はここにあります、
#if DEBUG
let a = 2
#else
let a = 3
#endif
以下のようにマクロを無効にすることができます、
#if !RELEASE
let a = 2
#else
let a = 3
#endif