1. Code
  2. Mobile Development
  3. iOS Development

Consejo rápido: personaliza NSLog para una depuración más sencilla

Scroll to top

Spanish (Español) translation by Santiago Diaz (you can also view the original English article)

En este consejo rápido, aprenderemos cómo personalizar la salida generada por NSLog para depurar programas de manera más eficiente. ¡Sigue leyendo!


Problema

De forma predeterminada, NSLog muestra la salida en el siguiente formato:

1
Date Time OurApp[<process­id>] NSLog output

Un ejemplo del mundo real podría verse así:

1
2013­08­03 00:35:53.038 TestApp[460:c07] Value of result = 20

La salida predeterminada es buena, pero deja algo que desear. La mayoría de las veces, queremos ver lo siguiente en una declaración de registro:

  • Nombre del archivo de origen donde se llamó a NSLog()
  • Número de línea de código fuente donde se llamó a NSLog()
  • Nombre de la clase y método donde se llamó a NSLog()
  • Oculta la marca de fecha y hora, el nombre de la aplicación y la información de identificación del proceso
  • Habilita/deshabilita la información de registro cambiando el modo (por ejemplo, depuración, liberación, puesta en escena)

En resumen, nos gustaría que NSLog fuera más así:

1
(ClassName MethodName) (SourceFileName:LineNumber) NSLog output

Solución

Primero veamos cómo funciona NSLog sin cambios. NSLog es solo una función de C integrada en el marco de trabajo básico de Cocoa y se comporta como cualquier otra función de C variable. Específicamente, NSLog envía mensajes de error a la función de registro del sistema de Apple. Lo hace simplemente pasando sus argumentos a la función NSLogv.

Debido a que NSLog es solo un contenedor para NSLogv, podemos redefinir NSLog con nuestra propia llamada personalizada a NSLogv. Eso es exactamente lo que te mostraré cómo hacer en este tutorial.


1. Inicia un nuevo proyecto

Crea un nuevo proyecto de iOS en Xcode, con la plantilla Aplicación vacía. Llámalo ExtendNSLog. Marca la opción de Recuento automático de referencias, pero desmarca las opciones de Datos básicos y Pruebas unitarias.

Crea un proyecto de iOS con la plantilla de aplicación vacía
El nombre del producto debe ser "ExtendNSLog"

2. Crea una clase Objective-C

Ahora crea un archivo de encabezado junto con el proyecto. Selecciona Nuevo archivo > Clase Objective-C. Establece el nombre de la clase en ExtendNSLogFunctionality. que será una subclase de NSObject.

Crea una plantilla de clase Objective-C
Establece el nombre de la clase en ExtendNSLogFunctionality

3. Agrega lógica NSLog personalizada

Paso 1

Abre ExtendNSLogFunctionality.h y coloca el siguiente código dentro del encabezado:

1
#import <Foundation/Foundation.h>
2
3
#ifdef DEBUG
4
#define NSLog(args...) ExtendNSLog(__FILE__,__LINE__,__PRETTY_FUNCTION__,args); 
5
#else
6
#define NSLog(x...)
7
#endif
8
9
void ExtendNSLog(const char *file, int lineNumber, const char *functionName, NSString *format, ...);

El condicional anterior definirá una declaración NSLog solo cuando se defina DEBUG. Cuando DEBUG no está definido, la instrucción NSLog no hará nada. Surge la pregunta: ¿cómo se controla cuándo se define DEBUG? Esto se puede hacer asignando DEBUG=1 en la configuración del preprocesador para tu proyecto.

Para hacerlo, haz clic en el destino de tu aplicación y selecciona la pestaña Configuración de compilación. A continuación, asegúrate de que las opciones "Todas" y "Combinadas" estén seleccionadas. Busca "preprocesamiento" y localiza la sección titulada "Macros de preprocesador". A continuación, simplemente agrega "DEBUG=1" a la sección Depuración.

Agrega el indicador DEBUG=1 a la configuración del preprocesador

Ten en cuenta que en las plantillas de proyecto de Xcode más recientes, ya habrá una macro DEBUG=1 definida para la configuración de compilación de depuración en la sección Macros de preprocesador. Para obtener más información, consulta esta publicación en StackOverflow.

Paso 2

Con la macro de depuración definida, nuestra siguiente tarea es escribir la versión personalizada de NSLog. Abre ExtendNSLogFunctionality.m y agrega el siguiente código:

1
#import "ExtendNSLogFunctionality.h"

2
3
void ExtendNSLog(const char *file, int lineNumber, const char *functionName, NSString *format, ...)
4
{
5
    // Type to hold information about variable arguments.

6
    va_list ap;
7
8
    // Initialize a variable argument list.

9
    va_start (ap, format);
10
    
11
    // NSLog only adds a newline to the end of the NSLog format if

12
    // one is not already there.

13
    // Here we are utilizing this feature of NSLog()

14
    if (![format hasSuffix: @"\n"])
15
    {
16
        format = [format stringByAppendingString: @"\n"];
17
    }
18
    
19
    NSString *body = [[NSString alloc] initWithFormat:format arguments:ap];
20
    
21
    // End using variable argument list.

22
    va_end (ap);
23
    
24
    NSString *fileName = [[NSString stringWithUTF8String:file] lastPathComponent];
25
    fprintf(stderr, "(%s) (%s:%d) %s",
26
            functionName, [fileName UTF8String],
27
            lineNumber, [body UTF8String]);
28
}

Paso 3

Ahora agrega la inclusión ExtendNSLogFunctionality.h al archivo de encabezado de prefijo Prefix.pch dentro de la sección #ifdef __OBJC__.

1
#ifdef __OBJC__

2
    #import <UIKit/UIKit.h>

3
    #import <Foundation/Foundation.h>

4
    #import "ExtendNSLogFunctionality.h"

5
#endif

Para una mejor comprensión de los encabezados de prefijo, echa un vistazo a esta entrada en Wikipedia. Con respecto a las mejores prácticas de encabezado de prefijo, consulta esta publicación de StackOverflow.


4. Un ejemplo de registro personalizado

Ahora agrega un NSLog en cualquier lugar del código de tu proyecto. En mi caso, decido agregar uno dentro del método de AppDelegate.m -(BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions.

1
int result = 20;
2
NSLog(@"Value of result : %d", result);

Si compilas y ejecutas el proyecto con la configuración de depuración ahora, deberías ver algo como esto:

1
(­[AppDelegate application:didFinishLaunchingWithOptions:]) (AppDelegate.m:21) Value of result : 20

¡Salud! Esta salida es mucho más útil que la implementación predeterminada. ¡Esperamos que descubras que esta técnica te ahorrará mucho tiempo mientras depuras tus propios programas!