web-dev-qa-db-ja.com

Swift言語の#ifdefの置き換え

C/C++/Objective-Cでは、コンパイラプリプロセッサを使ってマクロを定義できます。さらに、コンパイラプリプロセッサを使用してコードの一部を含める/除外することができます。

#ifdef DEBUG
    // Debug-only code
#endif

Swiftでも同様の解決策はありますか?

654
mxg

はい、できます。

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だけが動作します。コンパイラが特定の値を持つフラグを無視しているようです。

953
Jean Le Moignan

Apple Docs に記載されているように

Swiftコンパイラはプリプロセッサを含みません。代わりに、コンパイル時の属性、ビルド構成、および言語機能を利用して同じ機能を実現します。このため、Swiftではプリプロセッサディレクティブはインポートされません。

私は、カスタムビルド構成を使用して、自分が望んでいたことを達成できました。

  1. プロジェクトに移動する/ターゲットを選択する/ビルド設定/カスタムフラグを検索する
  2. 選択したターゲットに対して、デバッグとリリースの両方で、-D接頭辞(空白なし)を使用してカスタムフラグを設定します。
  3. あなたが持っているすべてのターゲットに対して上記の手順を実行します。

ターゲットを確認する方法は次のとおりです。

#if BANANA
    print("We have a banana")
#elseif MELONA
    print("Melona")
#else
    print("Kiwi")
#endif

enter image description here

Swift 2.2を使用してテスト済み

324
Andrej

多くの場合、条件付き コンパイル は必要ありません。あなただけの条件付き 動作 あなたがオンとオフを切り替えることができます必要があります。そのためには、環境変数を使うことができます。これには、実際に再コンパイルする必要がないという大きな利点があります。

スキームエディタで環境変数を設定し、簡単にオンまたはオフに切り替えることができます。

enter image description here

NSProcessInfoを使って環境変数を取得できます。

    let dic = NSProcessInfo.processInfo().environment
    if dic["TRIPLE"] != nil {
        // ... do secret stuff here ...
    }

これが現実の例です。私のアプリは、Simulatorには存在しないミュージックライブラリを使用しているため、デバイス上でのみ動作します。それでは、どのようにして私が所有していないデバイスのシミュレータでスクリーンショットを撮るのでしょうか。スクリーンショットがないと、AppStoreに送信できません。

偽のデータ それを処理する別の方法 が必要です。私は2つの環境変数を持っています。1つは、スイッチを入れると、私のデバイスで実行中に実際のデータから偽のデータを生成するようにアプリに指示します。もう1つは、シミュレータ上で実行されている間、電源を入れたときに、(見つからない音楽ライブラリではなく)偽のデータを使用します。 Schemeエディタの環境変数チェックボックスのおかげで、これらの特別モードのオン/オフを切り替えるのは簡単です。そして、アーカイブには環境変数がないため、App Storeのビルドで誤ってそれらを使用することはできません。

160
matt

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-改善点/

141
DShah

Swift 4.1では、コードがデバッグまたはリリース構成でビルドされているかどうかを確認するだけでよい場合は、組み込み関数を使用できます。

  • _isDebugAssertConfiguration()(最適化が-Ononeに設定されている場合はtrue)
  • _isReleaseAssertConfiguration()(最適化が-Oに設定されている場合はtrue) (Swift 3+では利用できません)
  • _isFastAssertConfiguration()(最適化が-Ouncheckedに設定されている場合はtrue)

例えば.

func obtain() -> AbstractThing {
    if _isDebugAssertConfiguration() {
        return DecoratedThingWithDebugInformation(Thing())
    } else {
        return Thing()
    }
}

プリプロセッサマクロと比較して、

  • ✓あなたはそれを使うためにカスタム-D DEBUGフラグを定義する必要はありません。
  • 〜これは実際にはXcodeビルド構成ではなく、最適化設定に関して定義されています
  • 文書化されていません。つまり、関数は更新のたびに削除できます(ただし、オプティマイザはこれらを定数に変換するため、AppStoreセーフである必要があります)。

  • if if/elseで使用すると、常に "実行されない"という警告が表示されます。

86
kennytm

Xcode 8以上

Build settings/Swiftコンパイラ - カスタムフラグActive Compilation Conditions設定を使用してください。

  • これは、Swiftコンパイラに条件付きコンパイルフラグを渡すための新しいビルド設定です。
  • ALPHABETAなどのように単純にフラグを追加します。

それからコンパイル条件のようにチェックしてください。

#if ALPHA
    //
#elseif BETA
    //
#else
    //
#endif

ヒント:#if !ALPHAなども使用できます

79
Jakub Truhlář

Swiftプリプロセッサはありません。 (1つには、任意のコードを代入すると、型およびメモリの安全性が損なわれます。)

ただし、Swiftにはビルド時の設定オプションが含まれているため、特定のプラットフォームやビルドスタイル、または-Dコンパイラ引数で定義したフラグに応じて、条件付きでコードを含めることができます。ただしCとは異なり、コードの条件付きコンパイル済みセクションは構文的に完全でなければなりません。これについてのセクションは CocoaとObjective-CでのSwiftの使用 にあります。

例えば:

#if os(iOS)
    let color = UIColor.redColor()
#else
    let color = NSColor.redColor()
#endif
73
rickster

Xcode 8の私の2セント:

a)-D接頭辞を使ったカスタムフラグはうまく働きますが、...

b)より簡単に使う:

Xcode 8には、デバッグとリリースのために、すでに2行の「アクティブコンパイル条件」という新しいセクションがあります。

-Dを付けずにdefineを追加するだけです。

45
ingconti

アクティブなコンパイル条件に基づくisDebug定数

コードベース全体で#if条件式をこじ開けずに関数に渡すことができるブール値になる可能性があるもう1つの、おそらくもっと簡単な解決策は、DEBUGをプロジェクトビルドターゲットのActive Compilation Conditionsの1つとして定義し、次を含めることです:

#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ビルド構成ではなく、最適化設定に関して定義されています
  • Documented、これは関数が通常のAPIリリース/非推奨パターンに従うことを意味します。

  • ✓if/elseでnotを使用すると、「実行されません」という警告が生成されます。

38
Jon Willis

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が彼らの本のためにより詳細な内容と索引を書くことを願っています。

15
Vadim Motorine

Xコード9以上

#if DEVELOP
    //
#elseif PRODCTN
    //
#else
    //
#endif
8
midhun p

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

コンパイラは私のコードの構文をチェックするので、その構文が正しいことを確認してビルドします。

7
Rivera

![Xcode 8以降では、ビルド設定 - >カスタムフラグの検索]1

コード内

 #if Live
    print("Live")
    #else
    print("debug")
    #endif
2
sachin_kvk

これは 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は関数呼び出しを完全に排除しているように見えます。

1
Warren Stringer
func inDebugBuilds(_ code: () -> Void) {
    assert({ code(); return true }())
}

ソース

0
Adam Smaka

Moignans 答え ここでうまく動作します。これが役立つ場合のための情報のもう一つの平和はここにあります、

#if DEBUG
    let a = 2
#else
    let a = 3
#endif

以下のようにマクロを無効にすることができます、

#if !RELEASE
    let a = 2
#else
    let a = 3
#endif
0