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?
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.
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:
Voici comment vérifier la cible:
#if BANANA
print("We have a banana")
#elseif MELONA
print("Melona")
#else
print("Kiwi")
#endif
Testé avec Swift 2.2
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:
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.
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 /
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
)_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,
-D DEBUG
pour l'utiliserOcum 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).
@testable
, destin incertain pour le futur Swift.Utiliser in si/else générera toujours un avertissement "Ne sera jamais exécuté".
Utilisez les conditions de compilation actives dans Paramètres de construction/Swift compiler - Indicateurs personnalisés .
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.
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
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
.
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
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 ,
-D DEBUG
pour l'utiliser✓ 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é".
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.
XCODE 9 ET PLUS
#if DEVELOP
//
#elseif PRODCTN
//
#else
//
#endif
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.
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
! [Dans Xcode 8 et supérieur, allez dans le paramètre de construction -> recherche d'indicateurs personnalisés]1
Dans du code
#if Live
print("Live")
#else
print("debug")
#endif
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.
func inDebugBuilds(_ code: () -> Void) {
assert({ code(); return true }())
}