it-swarm.com.de

#ifdef Ersetzung in der Swift Sprache

In C/C++/Objective-C können Sie mithilfe von Compiler-Präprozessoren ein Makro definieren. Darüber hinaus können Sie mithilfe von Compiler-Präprozessoren einige Teile des Codes ein- oder ausschließen.

#ifdef DEBUG
    // Debug-only code
#endif

Gibt es eine ähnliche Lösung in Swift?

678
mxg

Ja, du kannst es schaffen.

In Swift können Sie weiterhin die Präprozessor-Makros "# if/# else/# endif" verwenden (obwohl eingeschränkter), wie in Apple docs angegeben. Hier ist ein Beispiel:

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

Jetzt müssen Sie jedoch das "DEBUG" -Symbol an einer anderen Stelle setzen. Stellen Sie es im Abschnitt "Swift Compiler - Custom Flags" in der Zeile "Other Swift Flags" ein. Sie fügen das DEBUG-Symbol mit dem Eintrag -D DEBUG hinzu.

Wie üblich können Sie im Debug- oder Release-Modus einen anderen Wert festlegen.

Ich habe es in echtem Code getestet und es funktioniert; es scheint jedoch auf einem Spielplatz nicht erkannt zu werden.

Sie können meinen ursprünglichen Beitrag lesen hier .


WICHTIGER HINWEIS: -DDEBUG=1 funktioniert nicht. Nur -D DEBUG funktioniert. Der Compiler scheint ein Flag mit einem bestimmten Wert zu ignorieren.

987
Jean Le Moignan

Wie in Apple Docs angegeben

Der Compiler Swift enthält keinen Präprozessor. Stattdessen werden Attribute zur Kompilierungszeit, Buildkonfigurationen und Sprachfunktionen verwendet, um die gleiche Funktionalität zu erzielen. Aus diesem Grund werden Präprozessor-Direktiven nicht in Swift importiert.

Ich habe es geschafft, mit benutzerdefinierten Build-Konfigurationen das zu erreichen, was ich wollte:

  1. Gehen Sie zu Ihrem Projekt, wählen Sie Ihr Ziel aus, erstellen Sie Einstellungen, und suchen Sie nach benutzerdefinierten Flags
  2. Setzen Sie für Ihr ausgewähltes Ziel Ihr benutzerdefiniertes Flag mit dem Präfix -D (ohne Leerzeichen) für Debug und Release
  3. Führen Sie die obigen Schritte für jedes Ziel aus, das Sie haben

So überprüfen Sie das Ziel:

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

enter image description here

Getestet mit Swift 2.2

331
Andrej

In vielen Situationen brauchen Sie nicht wirklich bedingte Kompilierung; Sie brauchen nur bedingte Verhalten, dass Sie ein- und ausschalten können. Dafür können Sie eine Umgebungsvariable verwenden. Dies hat den großen Vorteil, dass Sie nicht neu kompilieren müssen.

Sie können die Umgebungsvariable im Schema-Editor festlegen und ganz einfach ein- oder ausschalten:

enter image description here

Sie können die Umgebungsvariable mit NSProcessInfo abrufen:

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

Hier ist ein reales Beispiel. Meine App wird nur auf dem Gerät ausgeführt, da sie die Musikbibliothek verwendet, die im Simulator nicht vorhanden ist. Wie mache ich dann Screenshots im Simulator für Geräte, die ich nicht besitze? Ohne diese Screenshots kann ich mich nicht beim AppStore anmelden.

Ich brauche gefälschte Daten und andere Art der Verarbeitung. Ich habe zwei Umgebungsvariablen: eine, die die App beim Einschalten anweist, die gefälschten Daten aus den realen Daten zu generieren, während sie auf meinem Gerät ausgeführt werden; Die andere verwendet beim Einschalten die gefälschten Daten (nicht die fehlende Musikbibliothek), während sie auf dem Simulator ausgeführt wird. Das Ein- und Ausschalten dieser speziellen Modi ist dank der Kontrollkästchen für Umgebungsvariablen im Schema-Editor ganz einfach. Und der Bonus ist, dass ich sie nicht versehentlich in meinem App Store Build verwenden kann, da die Archivierung keine Umgebungsvariablen enthält.

165
matt

Eine wesentliche Änderung des ifdef -Ersatzes ergab sich mit Xcode 8. d. H. Verwendung von Active Compilation Conditions .

Siehe Erstellen und Verknüpfen in Xcode 8-Versionshinweis .

Neue Build-Einstellungen

Neue Einstellung: Swift_ACTIVE_COMPILATION_CONDITIONS

“Active Compilation Conditions” is a new build setting for passing conditional compilation flags to the Swift compiler.

Zuvor mussten wir Ihre bedingten Kompilierungsflags unter OTHER_Swift_FLAGS deklarieren und daran denken, der Einstellung "-D" voranzustellen. So kompilieren Sie beispielsweise bedingt mit einem MYFLAG-Wert:

#if MYFLAG1
    // stuff 1
#elseif MYFLAG2
    // stuff 2
#else
    // stuff 3
#endif

Der Wert, der zur Einstellung -DMYFLAG hinzugefügt werden soll

Jetzt müssen wir nur noch den Wert MYFLAG an die neue Einstellung übergeben. Es ist Zeit, alle diese bedingten Kompilierungswerte zu verschieben!

Weitere Swift Build-Einstellungen in Xcode 8 finden Sie unter folgendem Link: http://www.miqu.me/blog/2016/07/31/xcode-8-new- Build-Einstellungen-und-Analyzer-Verbesserungen/

147
DShah

Ab Swift 4.1 können Sie die integrierten Funktionen verwenden, wenn Sie nur überprüfen müssen, ob der Code mit Debug- oder Release-Konfiguration erstellt wurde:

  • _isDebugAssertConfiguration() (true, wenn die Optimierung auf -Onone eingestellt ist)
  • _isReleaseAssertConfiguration() (true, wenn die Optimierung auf -O gesetzt ist) (Nicht verfügbar für Swift 3+)
  • _isFastAssertConfiguration() (true, wenn die Optimierung auf -Ounchecked gesetzt ist)

z.B.

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

Im Vergleich zu Präprozessor-Makros

  • ✓ Sie müssen kein benutzerdefiniertes -D DEBUG -Flag definieren, um es zu verwenden
  • ~ Es wird in Bezug auf Optimierungseinstellungen definiert, nicht in Bezug auf die Xcode-Build-Konfiguration
  • ✗ Undokumentiert, was bedeutet, dass die Funktion bei jedem Update entfernt werden kann (sie sollte jedoch AppStore-sicher sein, da der Optimierer diese in Konstanten umwandelt).

  • ✗ Wenn Sie in if/else verwenden, wird immer die Warnung "Wird nie ausgeführt" ausgegeben.

88
kennytm

Xcode 8 und höher

Verwenden Sie die Einstellung Active Compilation Conditions in Build settings/Swift Compiler - Benutzerdefinierte Flags .

  • Dies ist die neue Buildeinstellung für die Übergabe bedingter Kompilierungsflags an den Compiler Swift.
  • Füge einfach Flags wie folgt hinzu: ALPHA, BETA usw.

Dann überprüfe es mit Kompilierungsbedingungen wie folgt:

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

Tipp: Sie können auch #if !ALPHA usw.

81
Jakub Truhlář

Es gibt keinen Swift Präprozessor. (Zum einen unterbricht die Ersetzung willkürlichen Codes die Typ- und Speichersicherheit.)

Swift enthält jedoch Konfigurationsoptionen für die Erstellungszeit, sodass Sie Code für bestimmte Plattformen oder Erstellungsstile oder als Reaktion auf Flags, die Sie mit den Compiler-Argumenten -D definieren, unter bestimmten Bedingungen einbinden können. Im Gegensatz zu C muss ein bedingt kompilierter Abschnitt Ihres Codes jedoch syntaktisch vollständig sein. Es gibt einen Abschnitt darüber in Verwenden von Swift mit Cocoa und Objective-C .

Zum Beispiel:

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

Meine zwei Cent für Xcode 8:

a) Ein benutzerdefiniertes Flag mit dem Präfix -D funktioniert einwandfrei, aber ...

b) Einfachere Verwendung:

In Xcode 8 gibt es einen neuen Abschnitt: "Active Compilation Conditions", bereits mit zwei Zeilen, zum Debuggen und Freigeben.

Fügen Sie einfach Ihre Definition WITHOUT -D hinzu.

47
ingconti

isDebug-Konstante basierend auf aktiven Kompilierungsbedingungen

Eine andere, vielleicht einfachere Lösung, die immer noch zu einem Booleschen Wert führt, den Sie an Funktionen übergeben können, ohne die Bedingungen für #if in Ihrer Codebasis zu ändern, besteht darin, DEBUG als einen der Werte für Active Compilation Conditions und Ihres Projektbuild-Ziels zu definieren füge folgendes hinzu (ich definiere es als globale Konstante):

#if DEBUG
    let isDebug = true
#else
    let isDebug = false
#endif

isDebug-Konstante basierend auf Compiler-Optimierungseinstellungen

Dieses Konzept baut auf kennytms Antwort auf

Der Hauptvorteil beim Vergleich mit Kennytmen ist, dass dies nicht auf privaten oder undokumentierten Methoden beruht.

In 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
}()

Im Vergleich zu Präprozessor-Makros und der Antwort von kennytm

  • ✓ Sie müssen kein benutzerdefiniertes -D DEBUG -Flag definieren, um es zu verwenden
  • ~ Es wird tatsächlich in Bezug auf Optimierungseinstellungen definiert, nicht in Bezug auf die Xcode-Build-Konfiguration
  • Dokumentiert , dh die Funktion folgt den normalen API-Release-/Verfallsmustern.

  • ✓ Wenn Sie in if/else nicht verwenden, wird die Warnung "Wird nie ausgeführt" ausgegeben.

41
Jon Willis

In Swift Projekten, die mit Xcode Version 9.4.1 erstellt wurden, Swift 4.1

#if DEBUG
#endif

funktioniert standardmäßig, da in den Präprozessor-Makros DEBUG = 1 bereits von Xcode gesetzt wurde.

Sie können also #if DEBUG "out of box" verwenden.

Übrigens, wie man die Bedingungskompilierungsblöcke im Allgemeinen verwendet, steht in Apples Buch The Swift Programming Language 4.1 (Abschnitt Compiler Control Statements) und wie man die Kompilierungsflags schreibt und was das Gegenstück zu C ist Makros in Swift sind in einem anderen Apple-Buch geschrieben. Verwenden von Swift mit Cocoa und Objective C (im Abschnitt Präprozessor-Direktiven)

Hoffe in Zukunft Apple wird die detaillierteren Inhalte und die Indizes für ihre Bücher schreiben.

20
Vadim Motorine

XCODE 9 UND OBEN

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

Nachdem Sie DEBUG=1 in Ihrem GCC_PREPROCESSOR_DEFINITIONS Build-Einstellungen festgelegt haben, bevorzuge ich die Verwendung einer Funktion für diese Aufrufe:

func executeInProduction(_ block: () -> Void)
{
    #if !DEBUG
        block()
    #endif
}

Und schließen Sie dann in diese Funktion einfach jeden Block ein, den ich in Debug-Builds weglassen möchte:

executeInProduction {
    Fabric.with([Crashlytics.self]) // Compiler checks this line even in Debug
}

Der Vorteil gegenüber:

#if !DEBUG
    Fabric.with([Crashlytics.self]) // This is not checked, may not compile in non-Debug builds
#endif

Ist das der Compiler die Syntax meines Codes überprüft, so bin ich sicher, dass seine Syntax korrekt ist und baut.

7
Rivera

Moignans Antwort hier funktioniert gut. Hier ist eine weitere Info, falls es hilft,

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

Sie können die Makros wie folgt negieren,

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

Dies baut auf Jon Willis einer Antwort auf, die auf assert beruht und nur in Debug-Kompilierungen ausgeführt wird:

func Log(_ str: String) { 
    assert(DebugLog(str)) 
}
func DebugLog(_ str: String) -> Bool { 
    print(str) 
    return true
}

Mein Anwendungsfall ist die Protokollierung von Druckanweisungen. Hier ist ein Benchmark für die Release-Version auf dem 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)" )

druckt:

Log: 0.0

Sieht aus wie Swift 4 den Funktionsaufruf vollständig beseitigt.

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

Quelle

0
Adam Smaka