Je voudrais ignorer globalement tous les appels println()
dans mon code Swift si je ne suis pas dans une construction Debug. Je ne trouve pas d'instructions détaillées étape par étape pour cela et apprécierais des conseils. Est-il possible de le faire globalement ou dois-je entourer chaque println()
par des instructions #IF DEBUG/#ENDIF
?
Comme indiqué, je suis un étudiant et j'ai besoin que les choses soient définies un peu plus clairement pour pouvoir les suivre. Après de nombreuses recherches, la séquence que je devais suivre était la suivante:
Cliquez sur le nom du projet en haut du navigateur de fichiers, à gauche de la fenêtre du projet Xcode. Cette ligne porte le nom du projet, le nombre de cibles de génération et la version du SDK iOS.
Choisissez l'onglet Paramètres de construction et faites défiler jusqu'à la section " Compilateur rapide - Indicateurs personnalisés " située près du bas. Cliquez sur la flèche vers le bas en regard de Autres drapeaux pour agrandir la section.
Cliquez sur la ligne Debug pour le sélectionner. Placez le curseur de votre souris sur le côté droit de la ligne et double-cliquez dessus. Une liste apparaîtra. Cliquez sur le bouton + en bas à gauche de la liste pour ajouter une valeur. Un champ de texte deviendra actif.
Dans le champ de texte, entrez le texte -D DEBUG
et appuyez sur Retour pour valider la ligne.
Ajoutez un nouveau fichier Swift à votre projet. Vous allez vouloir créer une classe personnalisée pour le fichier, entrez donc le texte comme suit:
class Log {
var intFor : Int
init() {
intFor = 42
}
func DLog(message: String, function: String = __FUNCTION__) {
#if DEBUG
println("\(function): \(message)")
#endif
}
}
J'avais du mal à faire accepter le cours aujourd'hui par Xcode, donc l'init peut être un peu plus lourd que nécessaire.
Vous devez maintenant référencer votre classe personnalisée dans toute classe dans laquelle vous souhaitez utiliser la nouvelle fonction personnalisée à la place de println()
. Ajoutez ceci en tant que propriété dans chaque classe applicable:
let logFor = Log()
Vous pouvez maintenant remplacer toutes les instances de println()
par logFor.DLog()
. La sortie inclut également le nom de la fonction dans laquelle la ligne a été appelée.
Notez que dans les fonctions de classe, je ne pouvais pas appeler la fonction à moins de faire une copie de la fonction en tant que fonction de classe dans cette classe, et println()
est également un peu plus flexible avec l'entrée; dans mon code.
Le moyen le plus simple est de placer votre propre fonction globale devant la println
de Swift:
func println(object: Any) {
Swift.println(object)
}
Quand il est temps d'arrêter de vous connecter, commentez le corps de cette fonction:
func println(object: Any) {
// Swift.println(object)
}
Ou vous pouvez le rendre automatique en utilisant un conditionnel:
func println(object: Any) {
#if DEBUG
Swift.println(object)
#endif
}
EDITDans Swift 2.0, println
est remplacé par print
. Malheureusement, il a maintenant un premier paramètre variadique; c’est cool, mais cela signifie que vous ne pouvez pas le remplacer facilement car Swift n’a pas d’opérateur "splat", vous ne pouvez donc pas transmettre de code variadique (il ne peut être créé que littéralement). Mais vous pouvez créer une version réduite qui fonctionne si, comme ce sera généralement le cas, vous n'imprimez qu'une seule valeur:
func print(items: Any..., separator: String = " ", terminator: String = "\n") {
Swift.print(items[0], separator:separator, terminator: terminator)
}
Dans Swift 3, vous devez supprimer l’étiquette externe du premier paramètre:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
Swift.print(items[0], separator:separator, terminator: terminator)
}
Mise à jour pour Swift 4.x:
Swift 2.0/3.0 et Xcode 7/8 étant désormais hors de la version bêta, la façon dont vous désactivez la fonction d'impression dans les versions de version a été modifiée.
Certains points importants mentionnés par @matt et @Nate Birkholz ci-dessus sont toujours valables.
La fonction println()
a été remplacée par print()
Pour utiliser la macro #if DEBUG
, vous devez définir le "Compilateur Swift - Indicateurs personnalisés - Autres indicateurs" de manière à contenir la valeur -D DEBUG
.
Je recommanderais de remplacer la fonction Swift.print()
dans la portée globale afin que vous puissiez utiliser la fonction print()
comme d'habitude dans votre code, mais cela supprimera la sortie pour les versions non déboguées. Voici une signature de fonction que vous pouvez ajouter au niveau global pour le faire dans Swift 2.0/3.0:
func print(items: Any..., separator: String = " ", terminator: String = "\n") {
#if DEBUG
var idx = items.startIndex
let endIdx = items.endIndex
repeat {
Swift.print(items[idx], separator: separator, terminator: idx == (endIdx - 1) ? terminator : separator)
idx += 1
}
while idx < endIdx
#endif
}
Remarque: Nous avons défini le séparateur par défaut sur un espace et le terminateur par défaut sur une nouvelle ligne. Vous pouvez configurer cela différemment dans votre projet si vous le souhaitez.
J'espère que cela t'aides.
Mettre à jour:
Il est généralement préférable de placer cette fonction dans la portée globale afin qu'elle soit placée devant la fonction print
de Swift. Je trouve que la meilleure façon d’organiser cela consiste à ajouter un fichier d’utilitaire à votre projet (tel que DebugOptions.Swift) dans lequel vous pouvez placer cette fonction au niveau global.
A partir de Swift 3, l'opérateur ++
sera obsolète. J'ai mis à jour le fragment ci-dessus pour refléter ce changement.
Le problème avec toutes ces approches, y compris la mienne, est qu’elles ne suppriment pas la surcharge liée à l’évaluation des arguments print
. Peu importe lequel vous utilisez, cela va coûter cher:
print(myExpensiveFunction())
La seule solution décente consiste à encapsuler l'appel d'impression dans la compilation conditionnelle (supposons que DEBUG
soit défini uniquement pour les versions de débogage):
#if DEBUG
print(myExpensiveFunction())
#endif
Cela, et seulement cela, empêche myExpensiveFunction
d'être appelé dans une version release.
Cependant, vous pouvez repousser l'évaluation d'un niveau en utilisant autoclosure . Ainsi, vous pouvez réécrire ma solution (il s’agit de Swift 3) comme suit:
func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
#if DEBUG
Swift.print(item(), separator: separator, terminator: terminator)
#endif
}
Cela résout le problème dans le cas où vous imprimez une seule chose, ce qui est généralement vrai. C'est parce que item()
n'est pas appelé en mode release. print(myExpensiveFunction())
cesse donc d'être coûteux, car l'appel est encapsulé dans une fermeture sans être évalué, et en mode de libération, il ne sera pas évalué du tout.
Voici une fonction que j'utilise et qui fonctionne parfaitement dans Swift 3:
func gLog<T>( _ object: @autoclosure() -> T, _ file: String = #file, _ function: String = #function, _ line: Int = #line)
{
#if DEBUG
let value = object()
let stringRepresentation: String
if let value = value as? CustomDebugStringConvertible
{
stringRepresentation = value.debugDescription
}
else if let value = value as? CustomStringConvertible
{
stringRepresentation = value.description
}
else
{
fatalError("gLog only works for values that conform to CustomDebugStringConvertible or CustomStringConvertible")
}
let fileURL = NSURL(string: file)?.lastPathComponent ?? "Unknown file"
let queue = Thread.isMainThread ? "UI" : "BG"
let gFormatter = DateFormatter()
gFormatter.dateFormat = "HH:mm:ss:SSS"
let timestamp = gFormatter.string(from: Date())
print("✅ \(timestamp) {\(queue)} \(fileURL) > \(function)[\(line)]: " + stringRepresentation + "\n")
#endif
}
Voici un exemple de la sortie qu'il génère:
Explication:
la coche verte est utilisée pour vous permettre de voir rapidement vos messages d'impression (gLog) dans la console, où ils peuvent parfois se perdre dans une mer d'autres messages
l'horodatage
le fil sur lequel il est exécuté - dans mon cas, il s’agit du MainThread (que j’appelle UI), ou pas du MainThread (que j’appelle BG, pour le fil d’arrière-plan)
le nom du fichier dans lequel se trouve le message gLog
la fonction dans le fichier dans laquelle se trouve le message gLog
le numéro de ligne du message gLog
le message gLog que vous souhaitez imprimer
J'espère que cela sera utile à quelqu'un d'autre!
Testé avec Swift 2.1 & Xcode 7.1.1
Il existe un moyen simple d'exclure toutes les instructions d'impression des versions, une fois que vous savez que les fonctions empty sont supprimées par le compilateur Swift.
Note latérale: à l'ère d'Objective-C, il existait un pré-analyseur qui pouvait être utilisé pour supprimer les instructions NSLog avant le compilateur, comme décrit dans ma réponse ici . a un pré-analyseur cette approche n’est plus valide.
Voici ce que j'utilise aujourd'hui comme fonction de journalisation avancée et facilement configurable, sans avoir à vous soucier de la supprimer dans les versions. De plus, en définissant différents indicateurs de compilateur, vous pouvez modifier les informations consignées selon vos besoins.
Vous pouvez modifier la fonction au besoin, toute suggestion d'amélioration est la bienvenue!
// Gobal log() function
//
// note that empty functions are removed by the Swift compiler -> use #if $endif to enclose all the code inside the log()
// these log() statements therefore do not need to be removed in the release build !
//
// to enable logging
//
// Project -> Build Settings -> Swift Compiler - Custom flags -> Other Swift flags -> Debug
// add one of these 3 possible combinations :
//
// -D kLOG_ENABLE
// -D kLOG_ENABLE -D kLOG_DETAILS
// -D kLOG_ENABLE -D kLOG_DETAILS -D kLOG_THREADS
//
// you can just call log() anywhere in the code, or add a message like log("hello")
//
func log(message: String = "", filePath: String = #file, line: Int = #line, function: String = #function) {
#if kLOG_ENABLE
#if kLOG_DETAILS
var threadName = ""
#if kLOG_THREADS
threadName = NSThread.currentThread().isMainThread ? "MAIN THREAD" : (NSThread.currentThread().name ?? "UNKNOWN THREAD")
threadName = "[" + threadName + "] "
#endif
let fileName = NSURL(fileURLWithPath: filePath).URLByDeletingPathExtension?.lastPathComponent ?? "???"
var msg = ""
if message != "" {
msg = " - \(message)"
}
NSLog("-- " + threadName + fileName + "(\(line))" + " -> " + function + msg)
#else
NSLog(message)
#endif
#endif
}
Voici où vous définissez les drapeaux du compilateur:
Un exemple de sortie avec tous les drapeaux ressemble à ceci:
2016-01-13 23:48:38.026 FoodTracker[48735:4147607] -- [MAIN THREAD] ViewController(19) -> viewDidLoad() - hello
Le code avec le log () ressemble à ceci:
override func viewDidLoad() { log("hello")
super.viewDidLoad()
// Handle the text field's user input through delegate callbacks
nameTextField.delegate = self
}
XCode 8 introduit quelques nouveaux paramètres de construction .
En particulier, une référence à Active Compilation Conditions
fait de manière similaire ce que les paramètres Other Flags ont fait.
"Conditions de compilation actives" est un nouveau paramètre de construction permettant de transmettre des indicateurs de compilation conditionnels au compilateur Swift.
Selon XCode 8 (testé en 8.3.2), vous obtiendrez ceci par défaut:
Donc, sans aucune configuration, vous pouvez écrire ce qui suit:
#if DEBUG
print("⚠️ Something weird happened")
#endif
Je vous recommande fortement de créer une classe/structure/fonction qui englobe cette logique de journalisation si vous utilisez cette approche de manière intensive. Vous voudrez peut-être prolonger cela plus tard.
Encore plus simple, après avoir vérifié que -D DEBUG
est défini pour les paramètres de construction OTHER_Swift_FLAGS
Debug:
#if !DEBUG
func println(object: Any) {}
func print(object: Any){}
#endif
Dans Swift 2/Xcode 7, vous n’avez plus besoin/n’utilisez plus println
mais vous pouvez ajouter des lignes pour:
func print(_ items: Any..., separator separator: String = default, terminator terminator: String = default)
Swift 4 Xcode 10.0
peut-être que tu pourrais utiliser ça
func dPrint(_ message: @autoclosure () -> Any) {
#if DEBUG
print(message())
#endif
}
La raison d'utiliser @autoclosure
est que si vous transmettez une fonction en tant que paramètre de message, la fonction sera appelée uniquement en mode débogage, ce qui affectera les performances.
contrairement à la fonction Swift.print(_ items: Any..., separator: String = default, terminator: String = default)
, ma solution ne comporte qu'un seul paramètre, car dans la plupart des cas, nous ne transmettons pas plusieurs paramètres, car la fonction print affiche uniquement les informations dans la console. Nous pouvons simplement convertir les paramètres en String: "\(param1)"+"\(param2)"
, à droite? espère que tu aimes ma solution
Swift 4.2
Le code ci-dessous fonctionne parfaitement pour moi:
func print(_ items: Any...) {
#if DEBUG
items.forEach { item in
Swift.print(item)
}
#endif
}
Imprimer chaque élément de cette manière supprime les crochets de tableau agaçants autour des instructions d'impression :)
Ma solution est d'utiliser ce code dans AppDelegate avant le cours.
// Disable console log in live app
#if !Arch(x86_64) && !Arch(i386)
public func debugPrint(items: Any..., separator: String = " ", terminator: String = "\n") {
}
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
}
#endif
class AppDelegate: UIResponder, UIApplicationDelegate {
// App Delegate Code
}
Vous pouvez définir debug_println
dont le contenu serait approximativement:
#if DEBUG
println()
#endif
pour ma solution je le simplifie
import UIKit
class DLog: NSObject {
init(title:String, log:Any) {
#if DEBUG
print(title, log)
#endif
}
}
puis pour le montrer il suffit d'appeler
_ = DLog(title:"any title", log:Any)