Avertissement: "format pas un littéral de chaîne et aucun argument de format"

110

Depuis la mise à niveau vers le dernier Xcode 3.2.1 et Snow Leopard, j'ai reçu l'avertissement

"format pas un littéral de chaîne et aucun argument de format"

à partir du code suivant:

NSError *error = nil;

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

}

Si errorMsgFormatest un NSStringavec des spécificateurs de format (par exemple:) "print me like this: %@", quel est le problème avec l' NSLogappel ci - dessus ? Et quelle est la méthode recommandée pour résoudre ce problème afin que l'avertissement ne soit pas généré?

Alexi Groove
la source

Réponses:

113

Emboitez-vous correctement vos supports? Je ne pense pas que j'aime NSLog()prendre un seul argument, c'est ce que vous lui transmettez. En outre, il effectue déjà le formatage pour vous. Pourquoi ne pas faire ça?

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

Ou, puisque vous dites qu'il errorMsgFormats'agit d'une chaîne de format avec un seul espace réservé, essayez-vous de le faire?

NSLog(@"%@, %@", [NSString stringWithFormat:errorMsgFormat, error], 
   [error userInfo]);              
Sixten Otto
la source
14
"Je ne pense pas que NSLog () aime prendre un seul argument" NSLog()peut prendre un argument, lorsque la chaîne de format ne contient aucun spécificateur de format.
user102008
Donne un autre argument de données d'avertissement non utilisé par la chaîne de format.
hasan
157

Xcode se plaint car c'est un problème de sécurité.

Voici un code similaire au vôtre:

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

Cette dernière instruction NSLog va exécuter l'équivalent de ceci:

NSLog(@"Jon Hess %@");

Cela va amener NSLog à rechercher un autre argument de chaîne, mais il n'y en a pas. En raison de la façon dont le langage C fonctionne, il va récupérer un pointeur de déchets aléatoire dans la pile et essayer de le traiter comme une NSString. Cela plantera très probablement votre programme. Maintenant, vos chaînes ne contiennent probablement pas de% @, mais un jour, elles le pourraient. Vous devez toujours utiliser une chaîne de format avec des données que vous contrôlez explicitement comme premier argument des fonctions qui acceptent des chaînes de format (printf, scanf, NSLog, - [NSString stringWithFormat:], ...).

Comme le souligne Otto, vous devriez probablement faire quelque chose comme:

NSLog(errorMsgFormat, error, [error userInfo]);
Jon Hess
la source
17
Et encore une fois sur SO, les réponses détaillées et bonnes tombent au bord du chemin. MERCI d'avoir expliqué cela complètement. Je n'aurais jamais compris ça.
Dan Rosenstark
38

Réponse finale: comme Jon Hess l'a dit, c'est un problème de sécurité car vous passez une chaîne WHATEVER à une fonction qui attend une chaîne de format. Autrement dit, il évaluera tous les spécificateurs de format DANS la chaîne de caractères. S'il n'y en a pas, c'est génial, mais s'il y en a, de mauvaises choses pourraient arriver.

La bonne chose à faire, alors, est d'utiliser directement une chaîne de format, par exemple

NSLog(@"%@", myNSString);

De cette façon, même s'il existe des spécificateurs de format dans myNSString, ils ne sont pas évalués par NSLog.

Alex Whittemore
la source
13

Je ne recommande pas spécialement de l'utiliser, car l'avertissement EST un vrai avertissement .. dans une utilisation dynamique du langage, il est possible de faire des choses à l'exécution sur la chaîne (c'est-à-dire insérer de nouvelles informations ou même planter le programme) .. Cependant, c'est possible pour forcer la suppression si vous SAVEZ que cela devrait être comme ça et que vous ne voulez vraiment pas être averti à ce sujet.

#pragma GCC diagnostic ignored "-Wformat-security"

Je dirais à GCC d'ignorer temporairement l'avertissement de compilation. Encore une fois, cela ne résout rien, mais il peut arriver que vous ne puissiez pas trouver un bon moyen de résoudre le problème.

EDIT: Depuis clang, le pragma a changé. Voir ceci: https://stackoverflow.com/a/17322337/3937

Qrikko
la source
10

Le moyen le plus rapide de résoudre ce problème serait d'ajouter @"%@",comme premier argument à votre NSLogappel, c'est-à-dire,

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

Cependant, vous devriez probablement considérer la réponse de Sixteen Otto.

Anthony Cramp
la source
10

Je viens de passer un zéro pour annuler les avertissements, peut-être que cela fonctionnerait pour vous?

NSLog (myString, nil);

Martytoof
la source
5
Quelqu'un peut-il expliquer POURQUOI le dépassement de zéro alors que le second paramètre résout l'avertissement?
cprcrack
1
Passer nil est explicite alors que l'absence d'un second paramètre ne l'est pas. Vous pouvez supposer que votre cheminée n'était pas allumée lorsque vous avez quitté la maison ou vous pouvez vous assurer que ce n'était pas le cas. Bien que rien ne se passe généralement parce que vous utilisez rarement votre foyer, ce sera la seule fois où votre maison brûle.
1
@SoldOutActivist Inutile. Le point non évident ici (pour quelqu'un qui ne vient pas d'un arrière-plan C) est la différence de comportement entre passer un nul explicite et ne rien passer, et votre commentaire n'explique pas cela.
Mark Amery
Bien: toute méthode Obj-C qui peut accepter un nombre variable d'arguments doit être explicitement terminée par nil. Ne rien passer n'est pas la même chose que passer nul. Passez du temps avec Obj-C et vous verrez cela encore et encore. La construction de tableaux est la plus courante.
3
Cela peut arrêter l'avertissement du compilateur, mais le problème sous-jacent, qui a été expliqué par Jon Hess , existe toujours - s'il y a plus d'un spécificateur de format dans myString, le premier ira bien, mais le second ramassera les déchets de la pile. La liste de substitution dans NSLog()n'est jamais nil terminée, @Vendu. Il existe deux options pour déterminer la longueur de la liste d'arguments: une valeur sentinelle, ou ce qui est utilisé dans printf()et family - un autre argument qui permet le calcul du nombre (par exemple, en comptant les spécificateurs de format).
jscs
3

Si vous voulez vous débarrasser de l'avertissement "format pas un littéral de chaîne et aucun argument de format" une fois pour toutes, vous pouvez désactiver le paramètre d'avertissement GCC "Typecheck Calls to printf / scanf" (GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO) dans les paramètres de construction de votre cible.

aldi
la source
5
Cela fera taire l'avertissement, mais cela ne fera rien pour corriger la faille sous-jacente dans votre application. En faisant taire l'avertissement, vous ignorez un bogue potentiel qui pourrait planter votre application simplement sur la base des données saisies par l'utilisateur (ou dans ce cas le message d'erreur généré par CoreData). Il serait préférable de suivre certaines des autres réponses de cette question pour supprimer le bogue dans le code source à l'origine de l'avertissement.
Christopher Fairbairn
2
C'est vrai ... C'est pourquoi j'ai posté "se débarrasser de l'avertissement" au lieu de "résoudre".
aldi
J'ai rencontré un cas où la bibliothèque uthash déclenchait cet avertissement sur les appels à sa fonction utstring_printf, donc cela est utile dans les situations où l'avertissement est erroné.
alfwatt le
2

NSLog () attend une chaîne de format, ce qui est passé n'est qu'une chaîne. Vous n'avez pas besoin d'utiliser stringWithFormat :, vous pouvez simplement faire:

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

Et cela ferait disparaître l'avertissement.

Elfred
la source
2

FWIW, cela s'applique également aux développeurs iPhone. Je suis en train de coder avec le SDK 3.1.3 et j'ai eu la même erreur avec le même problème (nesting stringWithFormat dans NSLog ()). Sixten et Jon sont sur l'argent.

Pettiross
la source
0

Le simple fait de faire savoir à quiconque d'utiliser le appendFormaton NSMutableString peut également provoquer l'apparition de cet avertissement si vous essayez de transmettre une chaîne formatée comme ceci:

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

Donc, pour éviter cet avertissement, transformez ce qui précède en ceci:

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

Plus concis et plus sûr. Prendre plaisir!

ColossalChris
la source
-2
NSLog(@"%@ %@, %@", 
       errorMsgFormat, 
       error, 
       [error userInfo]); 
ILYA2606
la source
1
L'utilisation stringWithFormatest redondante ici alors que vous pouvez simplement le faireNSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])
Mark Amery