Stimmt es, dass man NSLog () nicht für Produktionscode verwenden sollte?

155

Das wurde mir ein paar Mal auf dieser Seite gesagt, aber ich wollte sicherstellen, dass dies wirklich der Fall ist.

Ich hatte erwartet, dass ich NSLog-Funktionsaufrufe in meinem Code verteilen kann und dass Xcode / gcc diese Aufrufe beim Erstellen meiner Release / Distribution-Builds automatisch entfernt.

Sollte ich dies vermeiden? Wenn ja, welche Alternativen sind bei erfahrenen Objective-C-Programmierern am häufigsten?

jpm
quelle
7
Ich weiß, dass diese Frage jetzt sehr alt ist, aber wenn Sie es noch können, würde ich Marc Charbonneaus Antwort als akzeptiert markieren. Ich habe meine Antwort geändert, um auf seine zu verweisen, aber seine Antwort ist die richtige.
e.James
5
NSLog () innerhalb einer häufigen Schleife wird Ihre Leistung absolut umbringen, sagte er, nachdem er es auf die harte Tour herausgefunden hatte.
Willc2

Antworten:

197

Präprozessor-Makros eignen sich hervorragend zum Debuggen. An NSLog () ist nichts auszusetzen, aber es ist einfach, eine eigene Protokollierungsfunktion mit besserer Funktionalität zu definieren. Hier ist die, die ich verwende. Sie enthält den Dateinamen und die Zeilennummer, um das Auffinden von Protokollanweisungen zu erleichtern.

#define DEBUG_MODE

#ifdef DEBUG_MODE
    #define DebugLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
    #define DebugLog( s, ... ) 
#endif

Ich fand es einfacher, diese gesamte Anweisung in den Präfix-Header einzufügen, als in eine eigene Datei. Wenn Sie möchten, können Sie ein komplizierteres Protokollierungssystem erstellen, indem Sie DebugLog mit normalen Objective-C-Objekten interagieren lassen. Beispielsweise könnten Sie eine Protokollierungsklasse haben, die in ihre eigene Protokolldatei (oder Datenbank) schreibt und ein Argument zur Priorität enthält, das Sie zur Laufzeit festlegen können, sodass Debug-Meldungen in Ihrer Release-Version nicht angezeigt werden, Fehlermeldungen jedoch ( Wenn Sie dies tun, können Sie DebugLog (), WarningLog () usw. erstellen.

Oh, und denken Sie daran, #define DEBUG_MODEdass es an verschiedenen Stellen in Ihrer Anwendung wiederverwendet werden kann. In meiner Anwendung verwende ich sie beispielsweise, um die Prüfung von Lizenzschlüsseln zu deaktivieren und die Ausführung der Anwendung nur zuzulassen, wenn sie vor einem bestimmten Datum liegt. Auf diese Weise kann ich mit minimalem Aufwand eine zeitlich begrenzte, voll funktionsfähige Beta-Kopie verteilen.

Marc Charbonneau
quelle
8
+1 für eine hervorragende Antwort. Ich habe meine geändert, um anzuzeigen, dass Ihre # Define-Makros der richtige Weg sind, und ich hoffe, dass das OP die akzeptierte Antwort wechselt (ich habe ihm einen Kommentar hinterlassen). Ich habe eine Dummy-Funktion verwendet, weil ich nicht wusste, dass Sie ... Argumente in einem Makro verwenden können. Leben lernen!
e.James
15
Eine ausgezeichnete Antwort, obwohl ich empfehle, ein persönliches Präfix für Ihre "DEBUG_MODE" -Definition zu verwenden, z. B. "JPM_DEBUG" oder ähnliches. Viel zu oft bin ich auf Code von Drittanbietern gestoßen, der auch DEBUG oder DEBUG_MODE oder ähnliches verwendet, und manchmal funktioniert dieser Code im DEBUG-Modus nicht richtig. Wenn Sie das Debuggen von Bibliotheken von Drittanbietern aktivieren möchten, sollten Sie dies absichtlich tun. (Natürlich sollten Bibliotheksschreiber ihren Symbolen ein Präfix voranstellen, aber viele C- und C ++ - Frameworks tun dies nicht, insbesondere für diese Definition).
Rob Napier
1
Gibt es ein vordefiniertes Xcode-Makro, mit dem dies nur aktiviert werden kann, wenn die Konfiguration auf Debug eingestellt ist? Ich möchte dieses Präprozessor-Makro lieber nicht in jedem Projekt selbst manuell einstellen. Können wir so etwas wie Pseudocode #if XCODE_CONFIGURATION == DEBUG folgen?
Frankodwyer
1
#include <TargetConditionals.h>
slf
2
Dieser Ansatz führt zu falschen "nicht verwendeten Variablen" -Warnungen des Compilers im Freigabemodus, wenn die Protokollierungsanweisungen nur Zwischenvariablen verwenden, um die zu protokollierenden Werte zu berechnen. Was wäre der klügste Weg, dies zu vermeiden, wenn Sie Compiler-Warnungen genauso hassen wie ich?
Jean-Denis Muys
78

Fügen Sie diese 3 Zeilen am Ende der Datei -prefix.pch ein:

#ifndef DEBUG
  #define NSLog(...) /* suppress NSLog when in release mode */
#endif

Sie müssen nichts in Ihrem Projekt DEBUGdefinieren , da dies beim Erstellen Ihres Projekts standardmäßig in Ihrer Build-Einstellung definiert wird.

roel
quelle
2
Weit die beste Lösung. Sie müssen prefix.pch manuell von XCode 6 hinzufügen.
Teddy
Müssen
25

NSLog-Aufrufe können im Produktionscode belassen werden, sollten jedoch nur für wirklich außergewöhnliche Fälle oder Informationen vorhanden sein, die im Systemprotokoll protokolliert werden sollen.

Anwendungen, die das Systemprotokoll verunreinigen, sind ärgerlich und wirken unprofessionell.

Matthew Schinckel
quelle
14
Entschuldigung - für wen unprofessionell rüberkommen? Wer überprüft wahrscheinlich Ihre Protokolle in einer veröffentlichten App und beurteilt Ihre Professionalität danach? (Um es klar auszudrücken, ich stimme voll und ganz zu, dass Sie nicht eine Menge NSLogs in der Release-Version Ihrer Anwendung behalten sollten, aber ich bin verwirrt über das Argument 'Professionalität'.)
WendiKidd
4
Andere Entwickler werden das tun, was Sie tun, und sich ärgern. Android hat ein ähnliches Problem mit einigen Entwicklern, die wirklich schlecht sind. Plus.google.com/110166527124367568225/posts/h4jK38n4XYR
Roger Binns
24

Ich kann die Antwort von Marc Charbonneau nicht kommentieren , daher werde ich dies als Antwort veröffentlichen.

Neben dem Hinzufügen des Makros zu Ihrem vorkompilierten Header können Sie die Target-Build-Konfigurationen verwenden, um die Definition (oder das Fehlen einer Definition) der zu steuern DEBUG_MODE.

Wenn Sie " Debug " auswählen , DEBUG_MODEwird die aktive Konfiguration definiert und das Makro wird zur vollständigen NSLogDefinition erweitert.

Die Auswahl der aktiven Konfiguration " Release " wird nicht definiert DEBUG_MODEund Ihr NSLogGing wird im Release-Build weggelassen.

Schritte:

  • Ziel> Informationen abrufen
  • Registerkarte "Erstellen"
  • Suchen Sie nach "PreProcessor Macros" (oder GCC_PREPROCESSOR_DEFINITIONS)
  • Wählen Sie Konfiguration: Debuggen
  • Definition auf dieser Ebene bearbeiten
  • Hinzufügen DEBUG_MODE=1
  • Wählen Sie Konfiguration: Freigeben
  • Bestätigen DEBUG_MODEist nicht eingestelltGCC_PREPROCESSOR_DEFINITIONS

Wenn Sie das Zeichen '=' in der Definition weglassen, wird vom Präprozessor eine Fehlermeldung angezeigt

Fügen Sie diesen Kommentar (siehe unten) über der Makrodefinition ein, um Sie daran zu erinnern, woher die DEBUG_MACRODefinition stammt.

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1
Ohhorob
quelle
1
Es ist eine wertvolle zusätzliche Antwort auf die Frage. Verdient mehr als ein Kommentar zu sein.
Morgenstern
DEBUG_MODEund DEBUG_MACROsind unkonventionell. Ich habe nur einen Verweis DEBUG_MACROauf der Apple-Website gefunden ( opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt ). Vielleicht der Standard DEBUGund NDEBUGwäre eine bessere Wahl? NDEBUGwird von Posix angegeben; während DEBUGwird durch Konvention verwendet.
JWW
+1 Ja, dies ist ein alter Beitrag, aber das ist der Punkt ... In meiner Version von Xcode (4 Jahre später) gibt eine Suche nach GCC_PREPROCESSOR_DEFINITIONS eine andere Sprache zurück. Bitte aktualisieren Sie diese hervorragende Antwort aus Gründen der Klarheit.
David
11

EDIT: Die Methode von Marc Charbonneau , auf die ich von sho aufmerksam gemacht wurde , ist weitaus besser als diese.

Ich habe den Teil meiner Antwort gelöscht, der die Verwendung einer leeren Funktion zum Deaktivieren der Protokollierung vorschlug, wenn der Debug-Modus deaktiviert ist. Der Teil, der sich mit dem Festlegen eines automatischen Präprozessormakros befasst, ist weiterhin relevant, bleibt also bestehen. Ich habe auch den Namen des Präprozessor-Makros so bearbeitet, dass er besser zu Marc Charbonneaus Antwort passt.


So erreichen Sie das automatische (und erwartete) Verhalten in Xcode:

Gehen Sie in den Projekteinstellungen zur Registerkarte "Erstellen" und wählen Sie die Konfiguration "Debuggen". Suchen Sie den Abschnitt "Präprozessor-Makros" und fügen Sie ein Makro mit dem Namen hinzu DEBUG_MODE.

...

EDIT: Siehe Marc Charbonneau Antwort für die richtige Art und Weise und Deaktivieren der Protokollierung mit dem aktivieren DEBUG_MODEMakro.

James
quelle
7

Ich stimme Matthew zu. An NSLog im Produktionscode ist nichts auszusetzen. In der Tat kann es für den Benutzer nützlich sein. Das heißt, wenn der einzige Grund, warum Sie NSLog verwenden, das Debuggen ist, sollte dies vor der Veröffentlichung entfernt werden.

Da Sie dies als iPhone-Frage markiert haben, nimmt NSLog außerdem Ressourcen in Anspruch, von denen das iPhone nur wenig hat. Wenn Sie auf dem iPhone NSLoggen , wird Ihre App dadurch weniger Prozessorzeit erhalten. Benutze es weise.

August
quelle
4

Die einfache Wahrheit ist, dass NSLog einfach nur langsam ist.

Aber wieso? Um diese Frage zu beantworten, wollen wir herausfinden, was NSLog macht und wie es dann geht.

Was macht NSLog genau?

NSLog macht zwei Dinge:

Es schreibt Protokollnachrichten in die Apple System Logging (ASL) -Funktion. Dadurch können Protokollnachrichten in Console.app angezeigt werden. Außerdem wird überprüft, ob der stderr-Stream der Anwendung zu einem Terminal geleitet wird (z. B. wenn die Anwendung über Xcode ausgeführt wird). In diesem Fall wird die Protokollnachricht in stderr geschrieben (sodass sie in der Xcode-Konsole angezeigt wird).

Das Schreiben an STDERR klingt nicht schwierig. Dies kann mit fprintf und der Stderr-Dateideskriptorreferenz erreicht werden. Aber was ist mit asl?

Die beste Dokumentation, die ich über ASL gefunden habe, ist ein 10-teiliger Blog-Beitrag von Peter Hosey: Link

Ohne zu sehr ins Detail zu gehen, ist das Highlight (was die Leistung betrifft) Folgendes:

Um eine Protokollnachricht an die ASL-Einrichtung zu senden, öffnen Sie grundsätzlich eine Clientverbindung zum ASL-Dämon und senden die Nachricht. ABER - jeder Thread muss eine separate Clientverbindung verwenden. Um Thread-sicher zu sein, öffnet NSLog bei jedem Aufruf eine neue ASL-Client-Verbindung, sendet die Nachricht und schließt die Verbindung.

Ressourcen finden Sie hier und hier .

Andrew
quelle
Text bearbeitet. Die Ressourcen müssen sich nur in der Fußzeile befinden.
Johan Karlsson
2

Wie in anderen Antworten angegeben, können Sie mit #define ändern, ob NSLog zur Kompilierungszeit verwendet wird oder nicht.

Eine flexiblere Möglichkeit besteht jedoch darin, eine Protokollierungsbibliothek wie Cocoa Lumberjack zu verwenden, mit der Sie ändern können, ob auch zur Laufzeit etwas protokolliert wird.

Ersetzen Sie in Ihrem Code NSLog durch DDLogVerbose oder DDLogError usw., fügen Sie einen #import für die Makrodefinitionen usw. hinzu und richten Sie die Protokollierer ein, häufig in der applicationDidFinishLaunching-Methode.

Um den gleichen Effekt wie NSLog zu erzielen, lautet der Konfigurationscode

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
mmmmmm
quelle
2

Aus Sicherheitsgründen hängt es davon ab, was protokolliert wird. Wenn NSLog(oder andere Protokollierer) vertrauliche Informationen schreiben, sollten Sie den Protokollierer im Produktionscode entfernen.

Aus Sicht der Prüfung möchte der Prüfer nicht jede Verwendung von NSLogprüfen, um sicherzustellen, dass keine vertraulichen Informationen protokolliert werden. Er / sie fordert Sie einfach auf, den Logger zu entfernen.

Ich arbeite mit beiden Gruppen. Wir prüfen Code, schreiben die Codierungshandbücher usw. Unser Handbuch erfordert, dass die Protokollierung im Produktionscode deaktiviert ist. Die internen Teams wissen also, dass sie es nicht versuchen sollen;)

Wir werden auch eine externe App ablehnen, die sich in der Produktion anmeldet, da wir das Risiko eines versehentlichen Verlusts vertraulicher Informationen nicht akzeptieren möchten. Es ist uns egal, was der Entwickler uns sagt. Es ist einfach nicht unsere Zeit wert, nachzuforschen.

Und denken Sie daran, wir definieren "sensibel" und nicht den Entwickler;)

Ich sehe auch eine App, die viel Protokollierung durchführt, als eine App, die zum Implodieren bereit ist. Es gibt einen Grund, warum so viel Protokollierung durchgeführt / benötigt wird und es normalerweise keine Stabilität gibt. Es ist genau dort oben mit 'Watchdog'-Threads, die hängende Dienste neu starten.

Wenn Sie noch nie eine Überprüfung der Sicherheitsarchitektur (SecArch) durchlaufen haben, sehen wir uns diese Dinge an.

jww
quelle
1

Sie sollten nicht unnötig ausführlich mit printf oder NSLog im Release-Code umgehen. Versuchen Sie nur, ein printf oder NSLog zu erstellen, wenn der App etwas Schlimmes passiert ist, dh ein nicht behebbarer Fehler.

MaddTheSane
quelle
1

Beachten Sie, dass NSLogs die Benutzeroberfläche / den Hauptthread verlangsamen können. Es ist am besten, sie aus Release-Builds zu entfernen, sofern dies nicht unbedingt erforderlich ist.

psy
quelle
0

Ich würde die Verwendung von TestFlight für die Protokollierung (kostenlos) wärmstens empfehlen. Ihre Methode überschreibt NSLog (mithilfe eines Makros) und ermöglicht es Ihnen, die Protokollierung auf ihrem Server, dem Apple System-Protokoll und dem STDERR-Protokoll für alle Ihre vorhandenen Aufrufe von NSLog ein- und auszuschalten. Das Schöne daran ist, dass Sie Ihre Protokollnachrichten weiterhin auf Apps überprüfen können, die für Tester und im App Store bereitgestellte Apps bereitgestellt wurden, ohne dass die Protokolle im Systemprotokoll des Benutzers angezeigt werden. Das Beste aus beiden Welten.

Joel
quelle
Man sollte den Overhead berücksichtigen, den TestFlight der Anwendung hinzufügt. Ist es möglich, nur den Protokollierungsteil von TestFlight hinzuzufügen?
Johan Karlsson