È vero che non si dovrebbe usare NSLog () sul codice di produzione?


155

Mi è stato detto alcune volte proprio in questo sito, ma volevo assicurarmi che fosse davvero così.

Mi aspettavo di essere in grado di diffondere le chiamate alla funzione NSLog in tutto il mio codice e che Xcode / gcc avrebbe automaticamente eliminato quelle chiamate durante la creazione delle build di Release / Distribution.

Dovrei evitare di usare questo? In tal caso, quali alternative sono più comuni tra i programmatori esperti di Objective-C?


7
So che questa domanda è ormai molto antica, ma, se è ancora possibile, segnerei la risposta di Marc Charbonneau come accettata. Ho modificato la mia risposta per indicare la sua, ma la sua risposta è quella corretta.
e.James

5
NSLog () all'interno di un ciclo frequente ucciderà assolutamente la tua esibizione, ha detto, dopo aver scoperto la strada difficile.
willc2,

Risposte:


197

Le macro del preprocessore sono davvero ottime per il debug. Non c'è niente di sbagliato in NSLog (), ma è semplice definire la propria funzione di registrazione con una migliore funzionalità. Ecco quello che uso, include il nome del file e il numero di riga per facilitare il rintracciamento delle istruzioni del registro.

#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

Ho trovato più facile inserire questa intera dichiarazione nell'intestazione del prefisso anziché nel suo file. Se lo si desidera, è possibile creare un sistema di registrazione più complicato facendo interagire DebugLog con normali oggetti Objective-C. Ad esempio, potresti avere una classe di registrazione che scrive nel proprio file di registro (o database) e include un argomento 'prioritario' che puoi impostare in fase di esecuzione, quindi i messaggi di debug non vengono visualizzati nella versione di rilascio, ma i messaggi di errore sono ( in questo caso è possibile creare DebugLog (), WarningLog () e così via).

Oh, e tieni presente che #define DEBUG_MODEpossono essere riutilizzati in diversi punti dell'applicazione. Ad esempio, nella mia applicazione lo uso per disabilitare i controlli delle chiavi di licenza e consentire l'esecuzione dell'applicazione solo se è precedente a una determinata data. Questo mi consente di distribuire una copia beta completamente limitata nel tempo, con il minimo sforzo da parte mia.


8
+1 per una risposta eccellente. Ho cambiato il mio per indicare che le tue macro #define sono la strada da percorrere, e spero che l'OP cambi la risposta accettata (gli ho lasciato un commento). Stavo usando una funzione fittizia perché non sapevo che potevi usare ... argomenti in una macro. Vivi e impara!
e.James

15
Una risposta eccellente, anche se consiglio di usare un prefisso personale sul tuo "DEBUG_MODE", come chiamarlo "JPM_DEBUG" o simili. Troppo spesso ho riscontrato codice di terze parti che utilizza anche DEBUG o DEBUG_MODE o simili, e talvolta quel codice non funzionerà correttamente in modalità DEBUG. Se si desidera attivare il debug della libreria di terze parti, è necessario farlo intenzionalmente. (Naturalmente, sono gli scrittori di biblioteche che dovrebbero anteporre i loro simboli, ma molti framework C e C ++ non lo fanno, specialmente per questo definire).
Rob Napier,

1
esiste una macro predefinita Xcode che può essere utilizzata per attivarla solo quando la configurazione è impostata su debug? Preferirei non impostare manualmente questa macro del preprocessore in ogni progetto. possiamo fare qualcosa come seguire lo pseudocodice # se XCODE_CONFIGURATION == DEBUG?
frankodwyer,

1
#include <TargetConditionals.h>
slf

2
Questo approccio porta ad avvisi spuri di "variabili non utilizzate" dal compilatore in modalità di rilascio quando le istruzioni di registrazione usano variabili intermedie al solo scopo di calcolare i valori da registrare. Quale sarebbe il modo più intelligente per evitarlo se odi gli avvisi del compilatore tanto quanto me?
Jean-Denis Muys,

78

Inserisci queste 3 righe alla fine del file -prefix.pch:

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

Non è necessario definire nulla nel progetto, poiché DEBUGè definito nelle impostazioni di creazione per impostazione predefinita quando si crea il progetto.


2
Di gran lunga la soluzione migliore. Devi aggiungere prefix.pch manualmente da XCode 6.
Teddy

Dobbiamo ancora modificare le impostazioni di compilazione prima del rilascio, ad es. Debug per rilasciarlo
UserDev il

25

Le chiamate NSLog possono essere lasciate nel codice di produzione, ma dovrebbero essere presenti solo per casi davvero eccezionali o informazioni che si desidera vengano registrate nel registro di sistema.

Le applicazioni che sporcano il registro di sistema sono fastidiose e risultano poco professionali.


14
Mi dispiace - ti sembra poco professionale per chi? Chi probabilmente controllerà i tuoi log su un'app rilasciata e giudicherà la tua professionalità in base a ciò? (Per essere chiari, sono totalmente d'accordo sul fatto che non dovresti tenere un sacco di NSLogs nella versione di rilascio della tua applicazione, ma sono confuso dall'argomento "professionalità".)
WendiKidd,

4
Altri sviluppatori faranno ciò che stai facendo e si arrabbieranno. Android ha un problema simile con alcuni sviluppatori molto cattivi plus.google.com/110166527124367568225/posts/h4jK38n4XYR
Roger Binns

24

Non posso commentare la risposta di Marc Charbonneau , quindi pubblicherò questo come risposta.

Oltre ad aggiungere la macro all'intestazione precompilata, è possibile utilizzare le configurazioni di build Target per controllare la definizione (o la mancanza di definizione) di DEBUG_MODE.

Se si seleziona la configurazione attiva " Debug ", DEBUG_MODEverrà definita e la macro si espande alla NSLogdefinizione completa .

La selezione della configurazione attiva " Rilascio " non definirà DEBUG_MODEe il tuo NSLogging verrà omesso dalla build del rilascio.

passi:

  • Target> Ottieni informazioni
  • Scheda Costruisci
  • Cerca "Macro preelaboratore" (o GCC_PREPROCESSOR_DEFINITIONS)
  • Seleziona Configurazione: Debug
  • Modifica definizione a questo livello
  • Inserisci DEBUG_MODE=1
  • Seleziona Configurazione: Rilascio
  • la conferma DEBUG_MODEnon è impostataGCC_PREPROCESSOR_DEFINITIONS

se si omette il carattere '=' nella definizione, si otterrà un errore dal preprocessore

Inoltre, incolla questo commento (mostrato sotto) sopra la definizione della macro per ricordare da dove viene la DEBUG_MACROdefinizione;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1

1
È una preziosa risposta aggiuntiva alla domanda. Merita di essere più di un commento.
morningstar,

DEBUG_MODEe DEBUG_MACROnon sono convenzionali. Ho trovato solo un riferimento a DEBUG_MACROsul sito di apple ( opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt ). Forse il più standard DEBUGe NDEBUGsarebbero le scelte migliori? NDEBUGè specificato da Posix; mentre DEBUGè usato per convenzione.
1313

+1 Sì, questo è un vecchio post, ma questo è il punto ... Nella mia versione di Xcode (4 anni dopo), una ricerca di GCC_PREPROCESSOR_DEFINITIONS restituisce una lingua diversa. Si prega di considerare l'aggiornamento di questa eccellente risposta per chiarezza.
David,

11

EDIT: Il metodo pubblicato da Marc Charbonneau e portato alla mia attenzione da sho , è di gran lunga migliore di questo.

Ho eliminato la parte della mia risposta che mi ha suggerito di utilizzare una funzione vuota per disabilitare la registrazione quando la modalità debug è disabilitata. La parte relativa all'impostazione di una macro del preprocessore automatico è ancora rilevante, quindi rimane. Ho anche modificato il nome della macro del preprocessore in modo che si adatti meglio alla risposta di Marc Charbonneau.


Per ottenere il comportamento automatico (e previsto) in Xcode:

Nelle impostazioni del progetto, vai alla scheda "Crea" e seleziona la configurazione "Debug". Trova la sezione "Macro del preprocessore" e aggiungi una macro denominata DEBUG_MODE.

...

EDIT: vedi la risposta di Marc Charbonneau per il modo corretto di abilitare e disabilitare la registrazione con la DEBUG_MODEmacro.


7

Sono d'accordo con Matthew. Non c'è niente di sbagliato in NSLog nel codice di produzione. In effetti, può essere utile per l'utente. Detto questo, se l'unico motivo per cui stai usando NSLog è aiutare il debug, allora sì, dovrebbe essere rimosso prima di rilasciarlo.

Inoltre, dal momento che hai etichettato questo come una domanda iPhone, NSLog prende risorse, che è qualcosa di cui l'iPhone ha poco di prezioso. Se stai registrando qualcosa su iPhone, ciò toglie il tempo del processore dalla tua app. Usalo saggiamente.


4

La semplice verità è che NSLog è semplicemente lento.

Ma perché? Per rispondere a questa domanda, scopriamo cosa fa NSLog e quindi come lo fa.

Cosa fa esattamente NSLog?

NSLog fa 2 cose:

Scrive i messaggi di registro nella funzione Apple System Logging (asl). Ciò consente ai messaggi di registro di apparire in Console.app. Controlla anche se lo stream stderr dell'applicazione sta andando su un terminale (come quando l'applicazione viene eseguita tramite Xcode). In tal caso scrive il messaggio di registro su stderr (in modo che venga visualizzato nella console Xcode).

Scrivere su STDERR non sembra difficile. Ciò può essere realizzato con fprintf e il riferimento al descrittore di file stderr. Ma che dire di slm?

La migliore documentazione che ho trovato su ASL è un post di blog in 10 parti di Peter Hosey: link

Senza entrare troppo nei dettagli, il momento saliente (per quanto riguarda le prestazioni) è questo:

Per inviare un messaggio di registro alla funzione ASL, fondamentalmente si apre una connessione client al demone ASL e si invia il messaggio. MA - ogni thread deve utilizzare una connessione client separata. Quindi, per essere thread-safe, ogni volta che viene chiamato NSLog apre una nuova connessione client asl, invia il messaggio e quindi chiude la connessione.

Le risorse possono essere trovate qui e qui .


Modificato il testo. Le risorse devono solo essere nel piè di pagina.
Johan Karlsson,

2

Come indicato in altre risposte, è possibile utilizzare un #define per modificare se NSLog viene utilizzato o meno al momento della compilazione.

Tuttavia, un modo più flessibile è quello di utilizzare una libreria di registrazione come Cocoa Lumberjack che ti consente di modificare se qualcosa è registrato anche in fase di esecuzione.

Nel tuo codice sostituisci NSLog con DDLogVerbose o DDLogError ecc., Aggiungi un #import per le definizioni macro ecc. E imposta i logger, spesso nel metodo applicationDidFinishLaunching.

Per avere lo stesso effetto di NSLog, il codice di configurazione è

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];

2

Dal punto di vista della sicurezza, dipende da cosa viene registrato. Se NSLog(o altri logger) stanno scrivendo informazioni riservate, è necessario rimuovere il logger nel codice di produzione.

Da un punto di vista dell'auditing, il revisore non vuole esaminare ogni utilizzo NSLogper assicurarsi che non registri informazioni sensibili. Ti dirà semplicemente di rimuovere il logger.

Lavoro con entrambi i gruppi. Controlliamo il codice, scriviamo le guide alla codifica, ecc. La nostra guida richiede che la registrazione sia disabilitata nel codice di produzione. Quindi i team interni sanno di non provarlo;)

Rifiuteremo anche un'app esterna che accede alla produzione perché non vogliamo accettare il rischio associato alla perdita accidentale di informazioni sensibili. Non ci interessa quello che ci dice lo sviluppatore. Semplicemente non vale il nostro tempo per indagare.

E ricorda, definiamo "sensibile" e non lo sviluppatore;)

Percepisco anche un'app che esegue molte registrazioni come un'app pronta a implodere. C'è un motivo per cui viene eseguita / necessaria la registrazione così tanto, e di solito non è stabilità. È proprio lì con thread 'watchdog' che riavviano i servizi sospesi.

Se non hai mai seguito una recensione di Security Architecture (SecArch), questi sono i tipi di cose che guardiamo.


1

Non dovresti essere inutilmente prolisso con printf o NSLog nel codice di rilascio. Prova a fare un printf o NSLog solo se l'app presenta qualcosa di brutto, ad esempio un errore irreversibile.


1

Tieni presente che NSLogs può rallentare l'interfaccia utente / thread principale. È meglio rimuoverli dalle build di rilascio se non assolutamente necessario.


0

Consiglio vivamente di utilizzare TestFlight per la registrazione (gratuito). Il loro metodo sovrascriverà NSLog (usando una macro) e ti permetterà di attivare / disattivare la registrazione sul loro server, il registro del sistema Apple e il registro STDERR, per tutte le chiamate esistenti a NSLog. La cosa bella di questo è che puoi ancora rivedere i tuoi messaggi di registro per le app distribuite ai tester e le app distribuite nell'App Store, senza che i registri vengano visualizzati nel registro di sistema dell'utente. Il meglio di entrambi i mondi.


Si dovrebbe considerare l'overhead che TestFlight aggiunge all'applicazione. È possibile aggiungere solo la parte di registrazione di TestFlight?
Johan Karlsson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.