Advertencia: "el formato no es una cadena literal ni argumentos de formato"

110

Desde que actualicé a la última versión de Xcode 3.2.1 y Snow Leopard, he recibido la advertencia

"formato no es una cadena literal ni argumentos de formato"

del siguiente código:

NSError *error = nil;

if (![self.managedObjectContext save:&error]) 
{
    NSLog([NSString stringWithFormat:@"%@ %@, %@", 
       errorMsgFormat, 
       error, 
       [error userInfo]]);      

}

Si errorMsgFormates un NSStringformato con especificadores (por ejemplo:) "print me like this: %@", ¿qué hay de malo en la NSLogllamada anterior ? ¿Y cuál es la forma recomendada de solucionarlo para que no se genere la advertencia?

Alexi Groove
fuente

Respuestas:

113

¿Está anidando sus soportes correctamente? No creo que NSLog()le guste tomar solo un argumento, que es lo que le está pasando. Además, ya realiza el formateo por ti. ¿Por qué no hacer esto?

NSLog(@"%@ %@, %@", 
   errorMsgFormat, 
   error, 
   [error userInfo]);              

O, como dice que errorMsgFormates una cadena de formato con un único marcador de posición, ¿está intentando hacer esto?

NSLog(@"%@, %@", [NSString stringWithFormat:errorMsgFormat, error], 
   [error userInfo]);              
Sixten Otto
fuente
14
"No creo que a NSLog () le guste tomar solo un argumento" NSLog()puede tomar un argumento, cuando la cadena de formato no contiene especificadores de formato.
user102008
Da otro argumento de datos de advertencia no utilizado por la cadena de formato.
Hasan
157

Xcode se queja porque se trata de un problema de seguridad.

Aquí hay un código similar al tuyo:

NSString *nameFormat = @"%@ %@";
NSString *firstName = @"Jon";
NSString *lastName = @"Hess %@";
NSString *name = [NSString stringWithFormat:nameFormat, firstName, lastName];
NSLog(name);

Esa última declaración NSLog ejecutará el equivalente a esto:

NSLog(@"Jon Hess %@");

Eso hará que NSLog busque un argumento de cadena más, pero no hay uno. Debido a la forma en que funciona el lenguaje C, tomará un puntero de basura aleatorio de la pila e intentará tratarlo como un NSString. Lo más probable es que esto bloquee su programa. Ahora tus cadenas probablemente no tengan% @ en ellas, pero algún día podrían tenerlas. Siempre debe usar una cadena de formato con datos que controle explícitamente como primer argumento para las funciones que toman cadenas de formato (printf, scanf, NSLog, - [NSString stringWithFormat:], ...).

Como señala Otto, probablemente deberías hacer algo como:

NSLog(errorMsgFormat, error, [error userInfo]);
Jon Hess
fuente
17
Y una vez más en SO, las respuestas detalladas y buenas se quedan en el camino. GRACIAS por explicar esto completamente. Nunca me hubiera dado cuenta de esto.
Dan Rosenstark
38

Respuesta final: como dijo Jon Hess, es un problema de seguridad porque está pasando una cadena CUALQUIER a una función que espera una cadena de formato. Es decir, evaluará todos los especificadores de formato DENTRO de la cadena. Si no los hay, genial, pero si los hay, pueden pasar cosas malas.

Entonces, lo correcto es USAR una cadena de formato directamente, por ejemplo

NSLog(@"%@", myNSString);

De esa manera, incluso si hay especificadores de formato en myNSString, NSLog no los evalúa.

Alex Whittemore
fuente
13

No recomiendo especialmente usar esto, ya que la advertencia ES una advertencia real ... en un uso dinámico del lenguaje es posible hacer cosas en tiempo de ejecución de la cadena (es decir, insertar nueva información o incluso bloquear el programa). Sin embargo, es posible forzar la supresión si SABES que debería ser así y realmente no quieres que te adviertan sobre ello.

#pragma GCC diagnostic ignored "-Wformat-security"

Le diría a GCC que ignore temporalmente la advertencia de compilación. Una vez más, no resuelve nada, pero puede haber ocasiones en las que no pueda encontrar una buena manera de solucionar el problema.

EDITAR: A partir de clang, el pragma ha cambiado. Vea esto: https://stackoverflow.com/a/17322337/3937

Qrikko
fuente
10

La forma más rápida de solucionarlo sería agregar @"%@",como primer argumento a su NSLogllamada, es decir,

NSLog(@"%@", [NSString stringWithFormat: ....]);

Sin embargo, probablemente deberías considerar la respuesta de Sixteen Otto.

Anthony Calambre
fuente
10

Acabo de pasar un cero para negar las advertencias, ¿tal vez eso funcione para usted?

NSLog (myString, nulo);

Martytoof
fuente
5
¿Alguien puede explicar POR QUÉ pasar nil como segundo paramente resuelve la advertencia?
cprcrack
1
Pasar nil es explícito, mientras que la falta de un segundo parámetro no lo es. Puede asumir que su chimenea no estaba encendida cuando salió de la casa o puede asegurarse de que no lo estuviera. Si bien no suele suceder nada porque rara vez usa su chimenea, será esa vez en la que su casa se incendie.
1
@SoldOutActivist Inútil. El punto no obvio aquí (para alguien que no proviene de un entorno C) es cuál es la diferencia de comportamiento entre pasar un nulo explícito y no pasar nada, y su comentario no lo explica.
Mark Amery
Bien: cualquier método Obj-C que pueda aceptar un número variable de argumentos debe terminarse explícitamente en nil. No pasar nada no es lo mismo que pasar nada. Pase cualquier tiempo con Obj-C y verá esto una y otra vez. La construcción de matrices es la más común.
3
Esto puede detener la advertencia del compilador, pero el problema subyacente, que fue explicado por Jon Hess , aún existe: si hay más de un especificador de formato myString, el primero estará bien, pero el segundo recogerá la basura de la pila. La lista de sustitución en NSLog()está no nil terminada en, @Sold. Hay dos opciones para averiguar qué tan larga es la lista de argumentos: un valor centinela, o lo que se usa en printf()y familia, otro argumento que permite calcular el número (por ejemplo, contando los especificadores de formato).
jscs
3

Si desea deshacerse de la advertencia "formato, no una cadena literal y sin argumentos de formato" de una vez por todas, puede desactivar la configuración de advertencia de GCC "Typecheck Calls to printf / scanf" (GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO) en la configuración de compilación de su objetivo.

aldi
fuente
5
Eso silenciará la advertencia, pero no hará nada para corregir la falla subyacente dentro de su aplicación. Al silenciar la advertencia, está ignorando un error potencial que podría bloquear su aplicación basándose simplemente en los datos ingresados ​​por el usuario (o en este caso, el mensaje de error generado por CoreData). Sería mejor seguir algunas de las otras respuestas dentro de esta pregunta para eliminar el error dentro del código fuente que está causando que aparezca la advertencia.
Christopher Fairbairn
2
Es cierto ... Por eso publiqué "deshacerse de la advertencia" en lugar de "resolver".
aldi
Me encontré con un caso en el que la biblioteca uthash activaba esta advertencia en las llamadas a su función utstring_printf, por lo que esto es útil en situaciones en las que la advertencia es incorrecta.
alfvatio
2

NSLog () espera una cadena de formato, lo que se pasa es solo una cadena. No es necesario utilizar stringWithFormat :, solo puede hacer:

NSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])

Y eso haría que la advertencia desapareciera.

Elfred
fuente
2

FWIW, esto también se aplica a los desarrolladores de iPhone. Estoy codificando contra el SDK 3.1.3 y obtuve el mismo error con el mismo problema (anidando stringWithFormat dentro de NSLog ()). Sixten y Jon están en el dinero.

Pettiross
fuente
0

El solo hecho appendFormatde informar a cualquiera que use on NSMutableString también puede hacer que aparezca esta advertencia si intenta pasar una cadena formateada como esta:

NSMutableString *csv = [NSMutableString stringWithString:@""];
NSString *csvAddition = [NSString stringWithFormat:@"%@",WHATEVERYOUAREPUTTINGINYOURSTRING];
[csv appendFormat:csvAddition];

Entonces, para evitar esta advertencia, convierta lo anterior en esto:

NSMutableString *csv = [NSMutableString stringWithString:@""];
[csv appendFormat:@"%@",WHATEVERYOUAREPUTTINGINYOURSTRING];

Más conciso y seguro. ¡Disfrutar!

ColosalChris
fuente
-2
NSLog(@"%@ %@, %@", 
       errorMsgFormat, 
       error, 
       [error userInfo]); 
ILYA2606
fuente
1
El uso stringWithFormates redundante aquí cuando podría hacerloNSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])
Mark Amery