NSLog el nombre del método con Objective-C en iPhone


153

Actualmente, nos estamos definiendo un mecanismo de registro extendido para imprimir el nombre de la clase y el número de línea de origen del registro.

#define NCLog(s, ...) NSLog(@"<%@:%d> %@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], \
    __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__])

Por ejemplo, cuando llamo a NCLog (@ "Hola mundo"); El resultado será:

<ApplicationDelegate:10>Hello world

Ahora también quiero desconectar el nombre del método como:

<ApplicationDelegate:applicationDidFinishLaunching:10>Hello world

Por lo tanto, esto facilitaría nuestra depuración cuando podamos saber qué método se llama. Sé que también tenemos el depurador Xcode, pero a veces, también quiero hacer la depuración cerrando sesión.


En mi último iPhoneproyecto, en realidad lo hice manualmente. Me encantaría ver la respuesta a esto.
Jacob Relkin

Respuestas:


261
print(__FUNCTION__) // Swift
NSLog(@"%@", NSStringFromSelector(_cmd)); // Objective-C

Swift 3 y superior

print(#function)

120
Realmente deberías usar NSLog(@"%@", NSStringFromSelector(_cmd)), si vas a usar _cmd, ya que AFAIK Apple declara _cmdcomo tipo SEL, no una C-string. El hecho de que se implemente como una cadena en C (a partir de las versiones actuales de Mac OS X y iPhone OS) no significa que deba usarlo de esa manera, ya que Apple podría cambiarlo en una actualización del sistema operativo.
Nick Forge

55
Sí, NSStringFromSelector es la respuesta más correcta. Nunca uso _cmd como cadena c para otra cosa que no sea código de depuración.
dibujado

Wow, el compilador se queja de la incompatibilidad del puntero, pero funciona ... Entonces _cmd (type: SEL) realmente es un char *!?
Nicolas Miari

3
Las llamadas a métodos como [self doSomething:arg1 somethingElse:arg2]se convierten en la llamada de función C objc_msgSend(self, "doSomething:somethingElse:, arg1, arg2);. El segundo parámetro de objc_msgSend()toma a char*. Recuerde que debido a que el tiempo de ejecución de Objective-C es dinámico, en realidad está usando una tabla de búsqueda para determinar qué método llamar a qué clase, por lo que un char * es conveniente ya que los métodos se representan como cadenas en la tabla de búsqueda.
Jack Lawrence el

1
Para Swift 2.2 debería usarprint("\(#function)")
Jake Lin

161

Para responder técnicamente a su pregunta, desea:

NSLog(@"<%@:%@:%d>", NSStringFromClass([self class]), NSStringFromSelector(_cmd), __LINE__);

O también podrías hacer:

NSLog(@"%s", __PRETTY_FUNCTION__);

2
Con __FUNCTION__y su bastante equivalente también está disponible en funciones C.
Georg Fritzsche

NB __FUNCTION__también incluye el nombre de la clase
OrangeDog

1
¿hay alguna diferencia usando NSLog (@ "% s", _func_ ); O NSLog (@ "% s", _PRETTY_FUNCTION_ ) ???
Ravi

83

tl; dr

NSLog( @"ERROR %@ METHOD %s:%d ", @"DescriptionGoesHere", __func__, __LINE__ );

Detalles

Apple tiene una página de preguntas y respuestas técnicas: QA1669 - ¿Cómo puedo agregar información de contexto, como el método actual o el número de línea, a mis declaraciones de registro?

Para ayudar con el registro:

  • El preprocesador C proporciona algunas macros .
  • Objective-C proporciona expresiones (métodos).
    • Pase el argumento implícito para el selector del método actual:_cmd

Como indican otras respuestas, para obtener simplemente el nombre del método actual, llame a:

NSStringFromSelector(_cmd)

Para obtener el nombre del método actual y el número de línea actual, use estas dos macros __func__y __LINE__como se ve aquí:

NSLog(@"%s:%d someObject=%@", __func__, __LINE__, someObject);

Otro ejemplo ... Fragmentos de código que guardo en la Biblioteca de fragmentos de código de Xcode:

NSLog( @"ERROR %@ METHOD %s:%d ", @"DescriptionGoesHere", __func__, __LINE__ );

... y TRACE en lugar de ERROR ...

NSLog( @"TRACE %@ METHOD %s:%d ", @"DescriptionGoesHere", __func__, __LINE__ );

... y una más larga con una descripción codificada que pasa un valor ( [rows count]) ...

NSLog( @"TRACE %@ METHOD %s:%d.", [NSString stringWithFormat:@"'Table of Contents.txt' file's count of Linefeed-delimited rows: %u.", [rows count]] , __func__, __LINE__ );

Macros de preprocesador para iniciar sesión

Tenga en cuenta el uso de un par de caracteres de subrayado en ambos lados de la macro.

El | Macro | Formato | Descripción
  __func__% s Firma de la función actual
  __LINE__% d Número de línea actual
  __FILE__% s Ruta completa al archivo fuente
  __PRETTY_FUNCTION__% s Como __func__, pero incluye detallado
                                    escriba información en código C ++. 

Expresiones para iniciar sesión

El | Expresión | Formato | Descripción
  NSStringFromSelector (_cmd)% @ Nombre del selector actual
  NSStringFromClass ([self class])% @ Nombre de clase del objeto actual
  [[NSString% @ Nombre del archivo de código fuente
    stringWithUTF8String: __ FILE__]   
    lastPathComponent] 
  [NSThread callStackSymbols]% @ NSArray de seguimiento de pila

Marcos de registro

Algunos marcos de registro también pueden ayudar a obtener el método actual o el número de línea. No estoy seguro, ya que utilicé un excelente marco de registro en Java ( SLF4J + LogBack ) pero no en Cocoa.

Vea esta pregunta para enlaces a varios marcos de registro de Cocoa.

Nombre del selector

Si tiene una variable Selector (un SEL ), puede imprimir su nombre de método ("mensaje") de cualquiera de las dos maneras que se describen en esta publicación de blog Codec :

  • Usando la llamada Objective-C a NSStringFromSelector :
    NSLog(@"%@", NSStringFromSelector(selector) );
  • Usando C recta:
    NSLog(@"%s", selector );

Esta información se extrajo de la página del documento de Apple vinculada a partir del 19/07/2013. Esa página se actualizó por última vez el 2011-10-04.


1
Para C, use sel_getName(SEL)ya que SEL es un tipo opaco y podría no ser siempre unchar *
Ethan Reesor

8
NSLog(@"%@", NSStringFromSelector(_cmd)); // Objective-C
print(__FUNCTION__) // Swift

44
Para cualquiera que se encuentre con esta respuesta en el futuro: es equivalente a la respuesta aceptada, pero la respuesta aceptada fue diferente cuando se publicó (la respuesta aceptada se editó en 2014). Estaba a punto de rechazar la votación, pero después de una pequeña investigación votó en su lugar :)
FreeNickname

0

En realidad es tan simple como:

printf(_cmd);

Por alguna razón, iOS permite que _cmd se pase como un carácter literal sin siquiera una advertencia de compilación. Quién sabe


0

En Swift 4:

prueba de función () {

print(#function)

}

test () // imprime el valor "test ()"

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.