¿Es cierto que no se debe usar NSLog () en el código de producción?

155

Me dijeron esto varias veces en este mismo sitio, pero quería asegurarme de que este sea realmente el caso.

Esperaba poder esparcir llamadas a la función NSLog en todo mi código, y que Xcode / gcc eliminaría automáticamente esas llamadas al compilar mis versiones de lanzamiento / distribución.

¿Debo evitar usar esto? Si es así, ¿qué alternativas son más comunes entre los programadores experimentados de Objective-C?

jpm
fuente
77
Sé que esta pregunta ahora es muy antigua, pero, si aún puede, marcaría la respuesta de Marc Charbonneau como aceptada. He modificado mi respuesta para señalar la suya, pero su respuesta es la correcta.
e.James
55
NSLog () dentro de un bucle frecuente matará absolutamente tu actuación, dijo, después de descubrirlo de la manera difícil.
willc2

Respuestas:

197

Las macros de preprocesador son realmente excelentes para la depuración. NSLog () no tiene nada de malo, pero es sencillo definir su propia función de registro con una mejor funcionalidad. Este es el que uso, incluye el nombre del archivo y el número de línea para que sea más fácil rastrear las declaraciones de 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

Me resultó más fácil poner esta declaración completa en el encabezado del prefijo en lugar de su propio archivo. Si lo desea, puede crear un sistema de registro más complicado haciendo que DebugLog interactúe con los objetos normales de Objective-C. Por ejemplo, podría tener una clase de registro que escriba en su propio archivo de registro (o base de datos) e incluya un argumento de 'prioridad' que podría establecer en tiempo de ejecución, por lo que los mensajes de depuración no se muestran en su versión de lanzamiento, pero los mensajes de error son ( Si hiciera esto, podría crear DebugLog (), WarningLog (), etc.).

Ah, y tenga en cuenta que #define DEBUG_MODEpuede reutilizarse en diferentes lugares de su aplicación. Por ejemplo, en mi aplicación lo uso para deshabilitar las comprobaciones de clave de licencia y solo permitir que la aplicación se ejecute si es anterior a una fecha determinada. Esto me permite distribuir una copia beta completamente funcional por tiempo limitado con un mínimo esfuerzo de mi parte.

Marc Charbonneau
fuente
8
+1 para una excelente respuesta. He cambiado la mía para indicar que sus #define macros son el camino a seguir, y espero que el OP cambie la respuesta aceptada (le dejé un comentario). Estaba usando una función ficticia porque no sabía que podía usar ... argumentos en una macro. ¡Vive aprende!
e.James
15
Una respuesta excelente, aunque recomiendo usar un prefijo personal en su definición "DEBUG_MODE", como llamarlo "JPM_DEBUG" o similar. Demasiado a menudo he encontrado código de terceros que también usa DEBUG o DEBUG_MODE o similares, y a veces ese código no funcionará correctamente en modo DEBUG. Si desea activar la depuración de bibliotecas de terceros, debe hacerlo intencionalmente. (Por supuesto, son los escritores de la biblioteca quienes deberían prefijar sus símbolos, pero muchos marcos C y C ++ no lo hacen, especialmente para esta definición).
Rob Napier
1
¿hay una macro predefinida de Xcode que se pueda usar para activar esto solo cuando la configuración está configurada para depurar? Prefiero no configurar manualmente esta macro de preprocesador en cada proyecto. ¿podemos hacer algo como seguir el pseudocódigo #if XCODE_CONFIGURATION == DEBUG?
frankodwyer
1
#include <TargetConditionals.h>
slf
2
Este enfoque lleva a advertencias falsas de "variables no utilizadas" del compilador en modo de lanzamiento cuando las declaraciones de registro usan variables intermedias con el único propósito de calcular los valores para registrar. ¿Cuál sería la forma más inteligente de evitar eso si odias las advertencias del compilador tanto como yo?
Jean-Denis Muys
78

Coloque estas 3 líneas al final del archivo -prefix.pch:

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

No necesita definir nada en su proyecto, porque DEBUGse define en su configuración de compilación de forma predeterminada cuando crea su proyecto.

roel
fuente
2
Lejos la mejor solución. Debe agregar prefix.pch manualmente desde XCode 6.
Teddy
Todavía necesitamos para ajuste de cambio de acumulación es decir, antes de la liberación de depuración para liberar
UserDev
25

Las llamadas de NSLog se pueden dejar en el código de producción, pero solo deben estar allí para casos realmente excepcionales, o la información que se desea que se registre en el registro del sistema.

Las aplicaciones que ensucian el registro del sistema son molestas y parecen poco profesionales.

Matthew Schinckel
fuente
14
Lo siento, ¿te parece poco profesional a quién? ¿Quién es probable que verifique sus registros en una aplicación lanzada y juzgue su profesionalidad en función de eso? (Para que quede claro, estoy totalmente de acuerdo en que no se debe mantener una tonelada de NSLogs en la versión de su aplicación, pero estoy confundido por el argumento 'profesionalismo'.)
WendiKidd
44
Otros desarrolladores harán lo que estás haciendo y se molestarán. Android tiene un problema similar con algunos desarrolladores realmente malos plus.google.com/110166527124367568225/posts/h4jK38n4XYR
Roger Binns
24

No puedo comentar sobre la respuesta de Marc Charbonneau , así que publicaré esto como respuesta.

Además de agregar la macro a su encabezado precompilado, puede usar las configuraciones de compilación de Target para controlar la definición (o la falta de definición) DEBUG_MODE.

Si selecciona la configuración activa " Depurar ", DEBUG_MODEse definirá y la macro se expandirá a la NSLogdefinición completa .

La selección de la configuración activa " Release " no definirá DEBUG_MODEy su NSLogging se omite de la versión de lanzamiento.

Pasos:

  • Target> Obtener información
  • Pestaña Construir
  • Busque "Macros de preprocesador" (o GCC_PREPROCESSOR_DEFINITIONS)
  • Seleccione Configuración: Depuración
  • Editar definición en este nivel
  • Añadir DEBUG_MODE=1
  • Seleccionar configuración: lanzamiento
  • confirmar DEBUG_MODEno está configurado enGCC_PREPROCESSOR_DEFINITIONS

si omite el carácter '=' en la definición, recibirá un error del preprocesador

Además, pegue este comentario (que se muestra a continuación) sobre la definición de macro para recordarle de dónde DEBUG_MACROviene la definición;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1
ohhorob
fuente
1
Es una valiosa respuesta adicional a la pregunta. Merece ser más que un comentario.
morningstar
DEBUG_MODEy DEBUG_MACROno son convencionales Solo encontré una referencia DEBUG_MACROen el sitio de apple ( opensource.apple.com/source/gm4/gm4-15/src/m4.h?txt ). Quizás el más estándar DEBUGy NDEBUGsería mejores opciones? NDEBUGes especificado por Posix; mientras DEBUGse usa por convención.
jww
+1 Sí, esta es una publicación antigua, pero ese es el punto ... En mi versión de Xcode (4 años después), una búsqueda de GCC_PREPROCESSOR_DEFINITIONS devuelve un idioma diferente. Considere actualizar esta excelente respuesta para mayor claridad.
David
11

EDIT: El método publicado por Marc Charbonneau , y trajo a mi atención por sho , es mucho mejor que éste.

He eliminado la parte de mi respuesta que sugería usar una función vacía para deshabilitar el registro cuando el modo de depuración está deshabilitado. La parte que se ocupa de establecer una macro de preprocesador automático sigue siendo relevante, por lo que permanece. También he editado el nombre de la macro del preprocesador para que encaje mejor con la respuesta de Marc Charbonneau.


Para lograr el comportamiento automático (y esperado) en Xcode:

En la configuración del proyecto, vaya a la pestaña "Compilar" y seleccione la configuración "Depurar". Busque la sección "Macros de preprocesador" y agregue una macro llamada DEBUG_MODE.

...

EDITAR: Vea la respuesta de Marc Charbonneau para conocer la forma correcta de habilitar y deshabilitar el registro con la DEBUG_MODEmacro.

e.James
fuente
7

Estoy de acuerdo con Matthew. No hay nada malo con NSLog en el código de producción. De hecho, puede ser útil para el usuario. Dicho esto, si la única razón por la que está usando NSLog es para ayudar a depurar, entonces, sí, eso debería eliminarse antes de liberarlo.

Además, dado que ha etiquetado esto como una pregunta de iPhone, NSLog toma recursos, que es algo de lo que el iPhone tiene muy poco. Si está registrando NSS cualquier cosa en el iPhone, eso le quita tiempo de procesador a su aplicación. Úsalo con sabiduría.

agosto
fuente
4

La simple verdad es que NSLog es simplemente lento.

¿Pero por qué? Para responder a esa pregunta, descubramos qué hace NSLog y cómo lo hace.

¿Qué hace exactamente NSLog?

NSLog hace 2 cosas:

Escribe mensajes de registro en la instalación de registro del sistema de Apple (ASL). Esto permite que los mensajes de registro aparezcan en Console.app. También verifica si el flujo stderr de la aplicación va a un terminal (como cuando la aplicación se ejecuta a través de Xcode). Si es así, escribe el mensaje de registro en stderr (para que aparezca en la consola Xcode).

Escribir a STDERR no suena difícil. Eso se puede lograr con fprintf y la referencia del descriptor de archivo stderr. ¿Pero qué hay de ASL?

La mejor documentación que he encontrado sobre ASL es una publicación de blog de 10 partes de Peter Hosey: enlace

Sin entrar en demasiados detalles, lo más destacado (en lo que respecta al rendimiento) es este:

Para enviar un mensaje de registro a la instalación de ASL, básicamente abre una conexión de cliente al demonio de ASL y envía el mensaje. PERO: cada subproceso debe usar una conexión de cliente separada. Por lo tanto, para ser seguro para subprocesos, cada vez que se llama NSLog, se abre una nueva conexión de cliente ASL, envía el mensaje y luego cierra la conexión.

Los recursos se pueden encontrar aquí y aquí .

Andrés
fuente
Editado el texto. Los recursos solo necesitan estar en el pie de página.
Johan Karlsson el
2

Como se señaló en otras respuestas, puede usar un #define para modificar si NSLog se usa o no en el momento de la compilación.

Sin embargo, una forma más flexible es utilizar una biblioteca de registro como Cocoa Lumberjack que le permite cambiar si algo se registra también en tiempo de ejecución.

En su código, reemplace NSLog por DDLogVerbose o DDLogError, etc., agregue una #importación para las definiciones de macro, etc. y configure los registradores, a menudo en el método applicationDidFinishLaunching.

Para tener el mismo efecto que NSLog, el código de configuración es

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

Desde el punto de vista de la seguridad, depende de lo que se registre. Si NSLog(u otros registradores) está escribiendo información confidencial, entonces debe eliminar el registrador en el código de producción.

Desde el punto de vista de la auditoría, el auditor no quiere examinar cada uso NSLogpara asegurarse de que no está registrando información confidencial. Él / ella simplemente le dirá que quite el registrador.

Yo trabajo con ambos grupos. Auditamos el código, escribimos las guías de codificación, etc. Nuestra guía requiere que el registro esté deshabilitado en el código de producción. Entonces los equipos internos saben que no deben intentarlo;)

También rechazaremos una aplicación externa que inicie sesión en producción porque no queremos aceptar el riesgo asociado con la filtración accidental de información confidencial. No nos importa lo que el desarrollador nos diga. Simplemente no vale la pena nuestro tiempo para investigar.

Y recuerde, definimos 'sensible', y no el desarrollador;)

También percibo una aplicación que realiza muchos registros como una aplicación lista para implosionar. Hay una razón por la que se realiza / necesita tanto registro, y generalmente no es estabilidad. Está a la altura de los hilos 'watchdog' que reinician los servicios bloqueados.

Si nunca ha pasado por una revisión de Arquitectura de seguridad (SecArch), este es el tipo de cosas que observamos.

jww
fuente
1

No debe ser innecesariamente detallado con printf o NSLog en el código de lanzamiento. Intente solo hacer un printf o NSLog si la aplicación le sucede algo malo, es decir, un error irrecuperable.

MaddTheSane
fuente
1

Tenga en cuenta que NSLogs puede ralentizar la interfaz de usuario / subproceso principal. Es mejor eliminarlos de las versiones de lanzamiento a menos que sea absolutamente necesario.

psy
fuente
0

Recomiendo usar TestFlight para iniciar sesión (gratis). Su método anulará NSLog (usando una macro) y le permitirá activar / desactivar el registro en su servidor, el registro del sistema Apple y el registro STDERR, para todas sus llamadas existentes a NSLog. Lo bueno de esto es que aún puede revisar sus mensajes de registro para las aplicaciones implementadas en los probadores y las aplicaciones implementadas en la App Store, sin que aparezcan los registros en el registro del sistema del usuario. Lo mejor de ambos mundos.

Joel
fuente
Uno debe considerar la sobrecarga que TestFlight agrega a la aplicación. ¿Es posible agregar solo la parte de registro de TestFlight?
Johan Karlsson el