web-dev-qa-db-fra.com

#ifdef remplacement dans la Swift langue

En C/C++/Objective-C, vous pouvez définir une macro à l'aide des pré-processeurs du compilateur. De plus, vous pouvez inclure/exclure certaines parties de code à l'aide de préprocesseurs de compilateur.

#ifdef DEBUG
    // Debug-only code
#endif

Existe-t-il une solution similaire à Swift?

678
mxg

Oui tu peux le faire.

Dans Swift, vous pouvez toujours utiliser les macros du préprocesseur "# if/# else/# endif" (bien que plus contraignantes), comme indiqué dans documents Apple . Voici un exemple:

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

Maintenant, vous devez définir le symbole "DEBUG" ailleurs. Définissez-le dans la section "Compilateur Swift - Indicateurs personnalisés", ligne "Autre Swift Drapeaux". Vous ajoutez le symbole DEBUG avec l'entrée -D DEBUG.

Comme d'habitude, vous pouvez définir une valeur différente dans Debug ou dans Release.

Je l'ai testé dans du code réel et cela fonctionne; cela ne semble pas être reconnu dans un terrain de jeu cependant.

Vous pouvez lire mon post original ici .


REMARQUE IMPORTANTE: -DDEBUG=1 ne fonctionne pas. Seul -D DEBUG fonctionne. Le compilateur semble ignorer un indicateur avec une valeur spécifique.

987
Jean Le Moignan

Comme indiqué dans Apple Docs

Le compilateur Swift n'inclut pas de préprocesseur. Au lieu de cela, il tire parti des attributs de compilation, des configurations de construction et des fonctionnalités de langage pour réaliser la même fonctionnalité. Pour cette raison, les directives du préprocesseur ne sont pas importées dans Swift.

J'ai réussi à réaliser ce que je voulais en utilisant des configurations de construction personnalisées:

  1. Accédez à votre projet/sélectionnez votre cible/Paramètres de construction/recherchez des indicateurs personnalisés.
  2. Pour la cible choisie, définissez votre drapeau personnalisé en utilisant le préfixe -D (sans espaces), pour Debug et Release.
  3. Faites les étapes ci-dessus pour chaque cible que vous avez

Voici comment vérifier la cible:

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

enter image description here

Testé avec Swift 2.2

331
Andrej

Dans de nombreuses situations, vous n'avez pas vraiment besoin de conditionnel compilation; vous avez juste besoin de conditionnel comportement que vous pouvez activer et désactiver. Pour cela, vous pouvez utiliser une variable d'environnement. Cela présente l’énorme avantage de ne pas avoir à recompiler.

Vous pouvez définir la variable d’environnement et l’activer ou la désactiver facilement dans l’éditeur de schéma:

enter image description here

Vous pouvez récupérer la variable d'environnement avec NSProcessInfo:

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

Voici un exemple concret. Mon application ne fonctionne que sur l'appareil, car elle utilise la bibliothèque de musique, qui n'existe pas sur le simulateur. Comment, alors, prendre des captures d'écran sur le simulateur pour des périphériques que je ne possède pas? Sans ces captures d'écran, je ne peux pas soumettre à l'AppStore.

J'ai besoin de fausses données et d'un manière différente de le traiter. J'ai deux variables d'environnement: l'une qui, lorsqu'elle est activée, indique à l'application de générer les fausses données à partir des données réelles lors de l'exécution sur mon appareil; l'autre qui, lorsqu'il est activé, utilise les fausses données (pas la bibliothèque de musique manquante) lors de l'exécution sur le simulateur. Activer/désactiver chacun de ces modes spéciaux est facile grâce aux cases à cocher de variables d’environnement de l’éditeur de schémas. Et le bonus est que je ne peux pas les utiliser accidentellement dans ma version d'App Store, car l'archivage n'a pas de variables d'environnement.

165
matt

Un changement majeur dans le remplacement de ifdef est venu avec Xcode 8. C'est-à-dire l'utilisation de Conditions de compilation actives .

Reportez-vous à Construction et liaison dans Note de version Xcode 8 .

Nouveaux paramètres de construction

Nouveau réglage: Swift_ACTIVE_COMPILATION_CONDITIONS

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

Auparavant, nous devions déclarer vos indicateurs de compilation conditionnelle sous OTHER_Swift_FLAGS, sans oublier d'ajouter le préfixe "-D" au paramètre. Par exemple, pour compiler de manière conditionnelle avec une valeur MYFLAG:

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

La valeur à ajouter au paramètre -DMYFLAG

Il ne nous reste plus qu'à passer la valeur MYFLAG au nouveau paramètre. Il est temps de déplacer toutes ces valeurs de compilation conditionnelles!

Veuillez vous référer au lien ci-dessous pour plus d'informations Swift Build Settings dans Xcode 8: http://www.miqu.me/blog/2016/07/31/xcode-8-new-build -settings-and-analyzer-améliorations /

147
DShah

A partir de Swift 4.1, si tout ce dont vous avez besoin est simplement de vérifier si le code est construit avec une configuration de débogage ou de publication, vous pouvez utiliser les fonctions intégrées:

  • _isDebugAssertConfiguration() (true lorsque l'optimisation est définie sur -Onone)
  • _isReleaseAssertConfiguration() (true lorsque l'optimisation est définie sur -O) (non disponible sur Swift 3+)
  • _isFastAssertConfiguration() (true lorsque l'optimisation est définie sur -Ounchecked)

par exemple.

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

Par rapport aux macros du préprocesseur,

  • ✓ Vous n'avez pas besoin de définir un drapeau personnalisé -D DEBUG pour l'utiliser
  • ~ Il est en fait défini en termes de paramètres d'optimisation, pas de configuration de construction Xcode
  • Ocum Non documenté, ce qui signifie que la fonction peut être supprimée dans n'importe quelle mise à jour (mais elle devrait être compatible avec AppStore, car l'optimiseur les transformera en constantes).

  • Utiliser in si/else générera toujours un avertissement "Ne sera jamais exécuté".

88
kennytm

Xcode 8 et plus

Utilisez les conditions de compilation actives dans Paramètres de construction/Swift compiler - Indicateurs personnalisés .

  • Il s'agit du nouveau paramètre de construction permettant de transmettre des indicateurs de compilation conditionnels au compilateur Swift.
  • Ajoutez simplement des drapeaux comme ceci: ALPHA, BETA etc.

Puis vérifiez avec conditions de compilation comme ceci:

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

Astuce: Vous pouvez également utiliser #if !ALPHA etc.

81
Jakub Truhlář

Il n'y a pas de préprocesseur Swift. (D'une part, la substitution de code arbitraire nuit à la sécurité du type et de la mémoire.)

Toutefois, Swift inclut des options de configuration au moment de la construction. Vous pouvez donc inclure conditionnellement du code pour certaines plates-formes ou certains styles de construction, ou en réponse aux indicateurs que vous définissez avec -D argument du compilateur. Contrairement à C, cependant, une section de votre code compilée sous condition doit être syntaxiquement complète. Il y a une section à ce sujet dans tiliser Swift avec Cocoa et Objective-C .

Par exemple:

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

Mes deux sous pour Xcode 8:

a) Un drapeau personnalisé utilisant le préfixe -D fonctionne bien, mais ...

b) Utilisation plus simple:

Dans Xcode 8, il existe une nouvelle section: "Conditions de compilation actives", comportant déjà deux lignes, pour le débogage et la publication.

Ajoutez simplement votre définition WITHOUT -D.

47
ingconti

constante isDebug basée sur des conditions de compilation actives

Une autre solution, peut-être plus simple, qui donne toujours un booléen que vous pouvez passer à des fonctions sans modifier les conditionnels #if dans votre base de code, consiste à définir DEBUG comme l'un des Active Compilation Conditions de votre cible de génération de projet. inclure les éléments suivants (je le définis comme une constante globale):

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

constante isDebug basée sur les paramètres d'optimisation du compilateur

Ce concept s'appuie sur réponse de kennytm

Le principal avantage lorsque l'on compare avec kennytm est que cela ne repose pas sur des méthodes privées ou non documentées.

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

Comparé aux macros du préprocesseur et à la réponse de kennytm ,

  • ✓ Vous n'avez pas besoin de définir un drapeau personnalisé -D DEBUG pour l'utiliser
  • ~ Il est en fait défini en termes de paramètres d'optimisation, pas de configuration de construction Xcode
  • Documenté , ce qui signifie que la fonction suivra les modèles de publication/dépréciation normaux de l'API.

  • ✓ Utiliser in si/else non génère un avertissement "Ne sera jamais exécuté".

41
Jon Willis

Dans Swift projets créés avec Xcode Version 9.4.1, Swift 4.1

#if DEBUG
#endif

fonctionne par défaut car dans les macros de préprocesseur, DEBUG = 1 a déjà été défini par Xcode.

Vous pouvez donc utiliser #if DEBUG "out of box".

À propos, comment utiliser les blocs de compilation de conditions en général est écrit dans le livre Le langage de programmation Swift 4.1 (la section Instructions de contrôle du compilateur) et comment écrire les indicateurs de compilation et les contreparties des macros C dans Swift est écrit dans un autre livre Apple Utilisation de Swift avec Cocoa et Objective C (dans la section Directives de préprocesseur)

Espérons que dans l'avenir Apple écrira le contenu plus détaillé et les index de leurs livres.

20
Vadim Motorine

XCODE 9 ET PLUS

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

Après avoir défini DEBUG=1 dans votre GCC_PREPROCESSOR_DEFINITIONS Réglages de construction, je préfère utiliser une fonction pour effectuer cet appel:

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

Et ensuite, vous devez inclure dans cette fonction tout bloc que je veux omettre dans les versions Debug:

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

L'avantage par rapport à:

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

Est-ce que le compilateur vérifie la syntaxe de mon code, alors je suis sûr que sa syntaxe est correcte et construit.

7
Rivera

Moignans réponse ici fonctionne bien. Voici une autre information en cas de besoin,

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

Vous pouvez nier les macros comme ci-dessous,

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

Cela s'appuie sur Jon Willis qui s'appuie sur assert, qui n'est exécuté que dans les compilations Debug:

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

Mon cas d'utilisation concerne la consignation des instructions d'impression. Voici une référence pour la version Release sur 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)" )

impressions:

Log: 0.0

On dirait que Swift 4 élimine complètement l'appel de fonction.

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

Source

0
Adam Smaka