Avviso: "formato non una stringa letterale e nessun argomento di formato"


110

Dall'aggiornamento all'ultimo Xcode 3.2.1 e Snow Leopard, ho ricevuto l'avviso

"formato non una stringa letterale e nessun argomento di formato"

dal seguente codice:

NSError *error = nil;

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

}

Se errorMsgFormatè un NSStringcon specificatori di formato (ad esempio:) "print me like this: %@", cosa c'è di sbagliato nella NSLogchiamata precedente ? E qual è il modo consigliato per risolverlo in modo che l'avviso non venga generato?

Risposte:


113

Stai annidando correttamente le parentesi? Non credo che gli NSLog()piaccia accettare un solo argomento, che è quello che stai passando. Inoltre, fa già la formattazione per te. Perché non farlo semplicemente?

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

Oppure, poiché dici che errorMsgFormatè una stringa di formato con un unico segnaposto, stai cercando di farlo?

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

14
"Non credo che a NSLog () piaccia prendere un solo argomento" NSLog()può accettare un argomento, quando la stringa di formato non contiene specificatori di formato.
user102008

Fornisce un altro avviso Argomento dati non utilizzato dalla stringa di formato.
hasan

157

Xcode si lamenta perché questo è un problema di sicurezza.

Ecco un codice simile al tuo:

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

L'ultima istruzione NSLog eseguirà l'equivalente di questo:

NSLog(@"Jon Hess %@");

Questo farà sì che NSLog cerchi un altro argomento stringa, ma non ce n'è uno. A causa del modo in cui funziona il linguaggio C, raccoglierà alcuni puntatori di spazzatura casuali dallo stack e proverà a trattarli come un NSString. Molto probabilmente questo farà andare in crash il tuo programma. Ora le tue stringhe probabilmente non contengono% @, ma un giorno potrebbero. Dovresti sempre usare una stringa di formato con i dati che controlli esplicitamente come primo argomento per le funzioni che accettano stringhe di formato (printf, scanf, NSLog, - [NSString stringWithFormat:], ...).

Come sottolinea Otto, probabilmente dovresti fare qualcosa del tipo:

NSLog(errorMsgFormat, error, [error userInfo]);

17
E ancora una volta su SO, le risposte dettagliate e buone cadono nel dimenticatoio. GRAZIE per averlo spiegato completamente. Non l'avrei mai capito.
Dan Rosenstark,

38

Risposta finale: come ha detto Jon Hess, è un problema di sicurezza perché stai passando QUALSIASI stringa a una funzione che si aspetta una stringa di formato. Cioè, valuterà tutti gli specificatori di formato ALL'INTERNO della stringa qualunque. Se non ce ne sono, fantastico, ma se ci sono, potrebbero accadere cose brutte.

La cosa giusta da fare, quindi, è USARE direttamente una stringa di formato, ad esempio

NSLog(@"%@", myNSString);

In questo modo, anche se sono presenti identificatori di formato in myNSString, non vengono valutati da NSLog.


13

Non consiglio particolarmente di usarlo, poiché l'avviso È un vero avvertimento .. in un uso dinamico del linguaggio è possibile fare cose runtime sulla stringa (cioè inserire nuove informazioni o addirittura mandare in crash il programma) .. Tuttavia è possibile per forzare la soppressione se SAPETE che dovrebbe essere così e davvero non volete essere avvertiti al riguardo ..

#pragma GCC diagnostic ignored "-Wformat-security"

Direbbe a GCC di ignorare temporaneamente l'avviso di compilazione .. Anche in questo caso non risolve nulla, ma a volte non è possibile trovare un buon modo per risolvere effettivamente il problema.

EDIT: A partire dal clang, il pragma è cambiato. Vedi questo: https://stackoverflow.com/a/17322337/3937


10

Il modo più rapido per risolverlo sarebbe aggiungere @"%@",come primo argomento alla tua NSLogchiamata, cioè

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

Tuttavia, dovresti probabilmente considerare la risposta di Sixteen Otto.


10

Ho appena passato uno zero per negare gli avvertimenti, forse potrebbe funzionare per te?

NSLog (myString, nil);


5
Qualcuno può spiegare PERCHÉ passare a zero mentre la seconda paramente risolve l'avvertimento?
cprcrack

1
Il passaggio a zero è esplicito mentre la mancanza di un secondo parametro non lo è. Puoi presumere che il tuo caminetto non fosse acceso quando sei uscito di casa o puoi assicurarti che non lo fosse. Anche se di solito non succede nulla perché usi raramente il tuo caminetto, sarà quella volta in cui la tua casa brucia.

1
@SoldOutActivist Unhelpful. Il punto non ovvio qui (per qualcuno che non proviene da uno sfondo C) è quale sia la differenza di comportamento tra passare un nil esplicito e non passare nulla, e il tuo commento non lo spiega.
Mark Amery

Fine: qualsiasi metodo Obj-C che possa accettare un numero variabile di argomenti deve essere esplicitamente terminato con zero. Passare niente non è come passare zero. Passa un po 'di tempo con Obj-C e lo vedrai ancora e ancora. La creazione di array è la più comune.

3
Questo potrebbe interrompere l'avviso del compilatore, ma il problema sottostante, spiegato da Jon Hess , esiste ancora: se c'è più di un identificatore di formato myString, il primo andrà bene, ma il secondo raccoglierà spazzatura dallo stack. L'elenco di sostituzione in nonNSLog() è mai nil terminato, @Sold. Ci sono due opzioni per capire quanto è lungo l'elenco di argomenti: un valore sentinella, o cosa è usato in printf()e family - un altro argomento che consente il calcolo del numero (ad esempio, contando gli specificatori di formato).
jscs

3

Se vuoi eliminare l'avvertimento "formato non una stringa letterale e nessun argomento di formato" una volta per tutte, puoi disabilitare l'impostazione di avviso di GCC "Typecheck Calls to printf / scanf" (GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO) nelle impostazioni di compilazione del tuo target.


5
Ciò silenzerà l'avviso, ma non farà nulla per correggere il difetto sottostante all'interno dell'applicazione. Silenziando l'avviso si ignora un potenziale bug che potrebbe mandare in crash la propria applicazione basandosi semplicemente sui dati inseriti dall'utente (o in questo caso il messaggio di errore generato da CoreData). Sarebbe meglio seguire alcune delle altre risposte all'interno di questa domanda per rimuovere il bug all'interno del codice sorgente che sta causando la visualizzazione dell'avviso.
Christopher Fairbairn,

2
Vero ... Ecco perché ho postato "elimina l'avviso" invece di "risolvi".
aldi

Mi sono imbattuto in un caso in cui la libreria uthash attivava questo avviso sulle chiamate alla sua funzione utstring_printf, quindi questo è utile in situazioni in cui l'avviso è sbagliato.
alfwatt

2

NSLog () si aspetta una stringa di formato, ciò che viene passato è solo una stringa. Non è necessario utilizzare stringWithFormat :, puoi semplicemente fare:

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

E questo farebbe scomparire l'avvertimento.


2

FWIW, questo vale anche per lo sviluppo di iPhone. Sto codificando contro l'SDK 3.1.3 e ho ricevuto lo stesso errore con lo stesso problema (annidamento di stringWithFormat all'interno di NSLog ()). Sixten e Jon sono sui soldi.


0

Anche solo far sapere a chiunque di utilizzare appendFormatNSMutableString può causare la visualizzazione di questo avviso se si tenta di passare una stringa formattata in questo modo:

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

Quindi, per evitare questo avviso, trasforma quanto sopra in questo:

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

Più conciso e più sicuro. Godere!


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

1
L'utilizzo stringWithFormatè ridondante qui quando si potrebbe semplicemente fareNSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])
Mark Amery
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.