wake-up-neo.com

Entfernen Sie println () für die Release-Version iOS Swift

Ich möchte alle println()-Aufrufe in meinem Swift-Code global ignorieren, wenn ich mich nicht in einem Debug-Build befinde. Ich kann dafür keine robuste Schritt-für-Schritt-Anleitung finden und würde mich über eine Anleitung freuen. Gibt es eine Möglichkeit, dies global zu tun, oder muss ich jede println() mit #IF DEBUG/#ENDIF-Anweisungen umgeben?

69
Nate Birkholz

Wie gesagt, ich bin Student und brauche etwas klareres, um weiterzuverfolgen. Nach vielen Recherchen war die Folge, der ich folgen musste:

Klicken Sie oben im File Navigator links im Xcode-Projektfenster auf den Projektnamen. Diese Zeile enthält den Namen des Projekts, die Anzahl der Build-Ziele und die iOS-SDK-Version.

Wählen Sie die Registerkarte Build Settings und blättern Sie zum unteren Abschnitt " Swift Compiler - Custom Flags ". Klicken Sie auf den Abwärtspfeil neben Other Flags , um den Abschnitt zu erweitern.

Klicken Sie auf die Zeile Debug , um sie auszuwählen. Platzieren Sie den Mauszeiger über der rechten Seite der Linie und doppelklicken Sie. Eine Listenansicht wird angezeigt. Klicken Sie unten links in der Listenansicht auf die Schaltfläche + , um einen Wert hinzuzufügen. Ein Textfeld wird aktiv.

Geben Sie im Textfeld den Text -D DEBUG ein und drücken Sie Return , um die Zeile zu bestätigen.

Fügen Sie Ihrem Projekt eine neue Swift-Datei hinzu. Sie möchten eine benutzerdefinierte Klasse für die Datei erstellen. Geben Sie daher Text entlang der folgenden Zeilen ein:

class Log {

  var intFor : Int

  init() {
    intFor = 42
   }

  func DLog(message: String, function: String = __FUNCTION__) {
    #if DEBUG
      println("\(function): \(message)")
    #endif
  }
}

Ich hatte Probleme, die Klasse heute von Xcode zu akzeptieren, daher ist die Init-Phase möglicherweise etwas schwerer als nötig. 

Jetzt müssen Sie Ihre benutzerdefinierte Klasse in jeder Klasse referenzieren, in der Sie anstelle von println() die neue benutzerdefinierte Funktion verwenden möchten. Fügen Sie dies in jeder anwendbaren Klasse als Eigenschaft hinzu:

   let logFor = Log()

Jetzt können Sie alle Instanzen von println() durch logFor.DLog() ersetzen. Die Ausgabe enthält auch den Namen der Funktion, in der die Zeile aufgerufen wurde.

Beachten Sie, dass ich die Funktion innerhalb von Klassenfunktionen nicht aufrufen konnte, es sei denn, ich habe eine Kopie der Funktion als Klassenfunktion in dieser Klasse erstellt. Außerdem ist println() etwas flexibler bei der Eingabe. Daher konnte ich diese Funktion nicht in allen Fällen verwenden in meinem Code.

16
Nate Birkholz

Am einfachsten stellen Sie Ihre eigene globale Funktion vor Swifts println:

func println(object: Any) {
    Swift.println(object)
}

Wenn es Zeit ist, die Protokollierung zu beenden, kommentieren Sie einfach den Hauptteil dieser Funktion aus:

func println(object: Any) {
    // Swift.println(object)
}

Oder Sie können es automatisch machen, indem Sie eine Bedingung verwenden:

func println(object: Any) {
    #if DEBUG
        Swift.println(object)
    #endif
}

EDITIn Swift 2.0 wird println in print geändert. Leider hat es jetzt einen variablen ersten Parameter; Das ist cool, aber es bedeutet, dass Sie es nicht leicht überschreiben können, weil Swift keinen "splat" -Operator hat, so dass Sie keine Variadic im Code übergeben können. Sie können jedoch eine reduzierte Version erstellen, die funktioniert, wenn Sie, wie üblich, nur einen Wert drucken:

func print(items: Any..., separator: String = " ", terminator: String = "\n") {
    Swift.print(items[0], separator:separator, terminator: terminator)
}

In Swift 3 müssen Sie die externe Beschriftung des ersten Parameters unterdrücken:

func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
    Swift.print(items[0], separator:separator, terminator: terminator)
}
94
matt

Aktualisiert für Swift 4.x:

Da Swift 2.0/3.0 und Xcode 7/8 nun nicht mehr in der Betaphase sind, wurden einige Änderungen an der Deaktivierung der Druckfunktion in Release-Builds vorgenommen.

Einige wichtige Punkte, die von @matt und @Nate Birkholz oben erwähnt wurden, sind noch gültig.

  1. Die Funktion println() wurde durch print() ersetzt.

  2. Um das Makro #if DEBUG verwenden zu können, müssen Sie die "Swift Compiler - Custom Flags -Other Flags" definieren, die den Wert -D DEBUG enthält.

  3. Ich würde empfehlen, die Funktion Swift.print() im globalen Gültigkeitsbereich zu überschreiben, sodass Sie die Funktion print() in Ihrem Code als normal verwenden können, die Ausgabe wird jedoch für Nicht-Debug-Builds entfernt. Hier ist eine Funktionssignatur, die Sie im globalen Bereich hinzufügen können, um dies in Swift 2.0/3.0 zu tun:

    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
    }
    

Hinweis: Wir haben hier das Standardtrennzeichen als Leerzeichen und das Standardabschlusszeichen als Newline festgelegt. Sie können dies in Ihrem Projekt anders konfigurieren, wenn Sie möchten.

Hoffe das hilft.

Update:

Es ist normalerweise vorzuziehen, diese Funktion in den globalen Bereich zu setzen, sodass sie vor Swifts print-Funktion steht. Ich finde, der beste Weg, dies zu organisieren, besteht darin, Ihrem Projekt eine Dienstprogrammdatei hinzuzufügen (wie DebugOptions.Swift), in der Sie diese Funktion im globalen Bereich platzieren können.

Ab Swift 3 wird der Operator ++ nicht mehr unterstützt. Ich habe den obigen Ausschnitt aktualisiert, um diese Änderung widerzuspiegeln.

42
Glavid

Das Problem bei all diesen Ansätzen, einschließlich meiner, besteht darin, dass sie den Aufwand für die Auswertung der print-Argumente nicht beseitigen. Egal welche davon Sie verwenden, das wird teuer:

print(myExpensiveFunction())

Die einzige vernünftige Lösung besteht darin, den tatsächlichen Druckaufruf in eine bedingte Kompilierung einzubinden (nehmen wir an, dass DEBUG nur für Debug-Builds definiert ist):

#if DEBUG
print(myExpensiveFunction())
#endif

Das und nur das verhindert, dass myExpensiveFunction in einem Release-Build aufgerufen wird.

Sie können die Auswertung jedoch um eine Stufe zurückschieben, indem Sie Autoclosure verwenden. So könnten Sie meine Lösung (das ist Swift 3) folgendermaßen umschreiben:

func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
    #if DEBUG
    Swift.print(item(), separator: separator, terminator: terminator)
    #endif
}

Dies löst das Problem nur für den Fall, dass Sie nur eine Sache drucken, was normalerweise der Fall ist. Das liegt daran, dass item() nicht im Freigabemodus aufgerufen wird. print(myExpensiveFunction()) ist somit nicht mehr teuer, da der Anruf ohne Auswertung in einen Abschluss eingepackt wird und im Freigabemodus überhaupt nicht ausgewertet wird.

31
matt

Hier ist eine Funktion, die ich benutze, die in Swift 3 perfekt funktioniert:

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
    }

Hier ist ein Beispiel für die Ausgabe, die es generiert:

 screenshot of output

Erläuterung:

  • mit dem grünen Häkchen können Sie Ihre Druckmeldungen (gLog) in der Konsole schnell sehen, wo sie manchmal in einer Fülle anderer Meldungen verloren gehen können

  • die Uhrzeit/Datumsstempel

  • der Thread, auf dem es ausgeführt wird - in meinem Fall ist es entweder das MainThread (das ich UI nenne) oder nicht das MainThread (das ich BG für Hintergrundthread nenne)

  • der Name der Datei, in der sich die gLog-Nachricht befindet

  • die Funktion innerhalb der Datei, in der sich die gLog-Nachricht befindet

  • die Zeilennummer der gLog-Nachricht

  • die eigentliche gLog-Nachricht, die Sie ausdrucken möchten

Hoffe, das ist jemand anderem nützlich!

9
Gene Loparco

Getestet mit Swift 2.1 & Xcode 7.1.1

Es gibt eine einfache Möglichkeit, alle Druckanweisungen von den Release-Versionen auszuschließen, sobald Sie wissen, dass empty-Funktionen vom Swift-Compiler entfernt werden.

Nebenbemerkung: In der Zeit von Objective-C gab es einen Vor-Parser, mit dem NSLog-Anweisungen entfernt werden konnten, bevor der Compiler eintrat, wie in meiner Antwort hier beschrieben. Aber seit Swift nicht mehr hat einen Vorparser, dieser Ansatz ist nicht mehr gültig.

Das ist, was ich heute als erweiterte und einfach konfigurierbare Protokollfunktion verwende, ohne dass ich mir jemals Gedanken darüber machen muss, ob sie in Release-Builds entfernt werden muss. Durch das Setzen verschiedener Compiler-Flags können Sie die protokollierten Informationen nach Bedarf anpassen.

Sie können die Funktion nach Bedarf anpassen, jeder Verbesserungsvorschlag ist willkommen!

// 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
}

Hier setzen Sie die Compiler-Flags:

 enter image description here

Eine Beispielausgabe mit allen Flags sieht folgendermaßen aus:

   2016-01-13 23:48:38.026 FoodTracker[48735:4147607] -- [MAIN THREAD] ViewController(19) -> viewDidLoad() - hello

Der Code mit dem Protokoll () sieht folgendermaßen aus:

    override func viewDidLoad() { log("hello")
    super.viewDidLoad()

   // Handle the text field's user input through delegate callbacks
   nameTextField.delegate = self
}
8
Ronny Webers

Mit XCode 8 wurden einige neue Buildeinstellungen eingeführt.
Insbesondere einer, auf den Active Compilation Conditions verwiesen wird, tut auf ähnliche Weise die Einstellungen von Other Flags.

"Active Compilation Conditions" ist eine neue Einstellung zum Übergeben bedingter Kompilierungsflags an den Swift-Compiler.

Gemäß XCode 8 (getestet in 8.3.2) erhalten Sie standardmäßig Folgendes:

 enter image description here

So können Sie ohne Konfiguration Folgendes schreiben:

#if DEBUG
    print("⚠️ Something weird happened")
#endif

Ich empfehle Ihnen dringend, wenn Sie diesen Ansatz ausführlich verwenden, erstellen Sie eine Klasse/Struktur/Funktion, die diese Protokollierungslogik umschließt. Möglicherweise möchten Sie dies auf der Straße erweitern.

6
Javier Cadiz

Noch einfacher, nachdem Sie sich vergewissert haben, dass -D DEBUG für die Buildeinstellungen für OTHER_Swift_FLAGS Debug festgelegt ist:

#if !DEBUG
    func println(object: Any) {}
    func print(object: Any){}
#endif

In Swift 2/Xcode 7 brauchen/verwenden Sie println nicht mehr, möchten jedoch Zeilen hinzufügen für:

func print(_ items: Any..., separator separator: String = default, terminator terminator: String = default)
4
Rivera

Swift 4 Xcode 10.0

vielleicht könntest du das gebrauchen

func dPrint(_ message: @autoclosure () -> Any) {
    #if DEBUG
    print(message())
    #endif
}

Der Grund für die Verwendung von @autoclosure ist, dass, wenn Sie eine Funktion als Nachrichtenparameter übergeben, die Funktion nur im Debug-Modus aufgerufen wird, dies einen Performance-Treffer verursacht.

im Gegensatz zur Swift.print(_ items: Any..., separator: String = default, terminator: String = default)-Funktion hat meine Lösung nur einen Parameter. In den meisten Fällen übergeben wir nicht mehrere Parameter, da die Druckfunktion nur Informationen in der Konsole anzeigt. Wir können die Parameter einfach in String konvertieren: "\(param1)"+"\(param2)", oder? hoffe du magst meine Lösung

2
Jiangshi Fresh

Swift 4.2

Der folgende Code funktioniert perfekt für mich:

func print(_ items: Any...) {
    #if DEBUG
    items.forEach { item in
        Swift.print(item)
    }
    #endif
}

Wenn Sie jedes Element so drucken, werden störende Array-Klammern um die Druckanweisungen entfernt :)

2
Trev14

Meine Lösung verwendet diesen Code in AppDelegate vor dem Unterricht

// 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 

}
0
Varun Naharia

Sie könnten debug_println definieren, dessen Inhalt ungefähr lauten würde:

#if DEBUG
  println()
#endif
0
Ian MacDonald

für meine Lösung mache ich es einfach

import UIKit

class DLog: NSObject {

   init(title:String, log:Any) {
       #if DEBUG
           print(title, log)
       #endif

   }

}

dann zeigen Sie es einfach an 

_ = DLog(title:"any title", log:Any)