Ostrzeżenie: „format to nie literał ciągu i brak argumentów formatu”

110

Od czasu aktualizacji do najnowszej wersji Xcode 3.2.1 i Snow Leopard otrzymuję ostrzeżenie

„format nie jest literałem ciągu i brak argumentów formatu”

z następującego kodu:

NSError *error = nil;

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

}

Jeśli errorMsgFormatjest NSStringz specyfikatorami formatu (np. "print me like this: %@":), Co jest nie tak z powyższym NSLogwywołaniem? Jaki jest zalecany sposób naprawienia tego problemu, aby ostrzeżenie nie było generowane?

Alexi Groove
źródło

Odpowiedzi:

113

Czy prawidłowo zagnieżdżasz zamki? Myślę, że nie NSLog()lubi brać tylko jednego argumentu, czyli tego, co mu zdajesz. Ponadto wykonuje już formatowanie za Ciebie. Dlaczego po prostu tego nie zrobić?

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

A może skoro mówisz, że errorMsgFormatjest to ciąg formatu z pojedynczym symbolem zastępczym, czy próbujesz to zrobić?

NSLog(@"%@, %@", [NSString stringWithFormat:errorMsgFormat, error], 
   [error userInfo]);              
Sixten Otto
źródło
14
„Nie sądzę, aby NSLog () lubił przyjmować tylko jeden argument” NSLog()może przyjąć jeden argument, gdy łańcuch formatu nie zawiera specyfikatorów formatu.
user102008
Daje kolejne ostrzeżenie Argument danych nie używany przez ciąg formatu.
hasan
157

Xcode narzeka, ponieważ jest to problem z bezpieczeństwem.

Oto kod podobny do twojego:

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

Ta ostatnia instrukcja NSLog będzie wykonywała odpowiednik tego:

NSLog(@"Jon Hess %@");

To spowoduje, że NSLog będzie szukał jeszcze jednego argumentu typu string, ale takiego nie ma. Ze względu na sposób działania języka C, pobierze on ze stosu jakiś losowy wskaźnik śmieci i spróbuje traktować go jak NSString. Najprawdopodobniej spowoduje to zawieszenie programu. Teraz twoje łańcuchy prawdopodobnie nie zawierają% @, ale któregoś dnia mogą. Zawsze należy używać łańcucha formatu z danymi, które jawnie kontrolujesz, jako pierwszego argumentu funkcji pobierających ciągi formatujące (printf, scanf, NSLog, - [NSString stringWithFormat:], ...).

Jak podkreśla Otto, prawdopodobnie powinieneś zrobić coś takiego:

NSLog(errorMsgFormat, error, [error userInfo]);
Jon Hess
źródło
17
I jeszcze raz w SO szczegółowe i dobre odpowiedzi odchodzą na dalszy plan. DZIĘKUJEMY za pełne wyjaśnienie. Nigdy bym tego nie rozgryzł.
Dan Rosenstark
38

Ostateczna odpowiedź: jak powiedział Jon Hess, jest to kwestia bezpieczeństwa, ponieważ przekazujesz ciąg WHATEVER do funkcji oczekującej ciągu formatu. Oznacza to, że oceni wszystkie specyfikatory formatu W RAMACH dowolnego ciągu. Jeśli ich nie ma, super, ale jeśli są, mogą się zdarzyć złe rzeczy.

Zatem właściwą rzeczą do zrobienia jest na przykład bezpośrednie UŻYCIE łańcucha formatującego

NSLog(@"%@", myNSString);

W ten sposób, nawet jeśli w myNSString istnieją specyfikatory formatu, nie są one oceniane przez NSLog.

Alex Whittemore
źródło
13

Nie polecam szczególnie tego używania, ponieważ ostrzeżenie JEST prawdziwym ostrzeżeniem .. w dynamicznym używaniu języka możliwe jest wykonanie pewnych czynności na łańcuchu (np. Wstawienie nowych informacji lub nawet zawieszenie programu) .. Jednak jest to możliwe wymusić tłumienie, jeśli WIESZ, że tak powinno być i naprawdę nie chcesz być o tym ostrzeżony.

#pragma GCC diagnostic ignored "-Wformat-security"

Powiedziałby GCC, aby tymczasowo zignorował ostrzeżenie o kompilacji. Ponownie, to niczego nie rozwiązuje, ale mogą wystąpić chwile, kiedy nie możesz znaleźć dobrego sposobu na faktyczne naprawienie problemu.

EDYCJA: Od czasu clang pragma uległa zmianie. Zobacz: https://stackoverflow.com/a/17322337/3937

Qrikko
źródło
10

Najszybszym sposobem rozwiązania tego problemu byłoby dodanie @"%@",jako pierwszego argumentu do NSLogwywołania, tj.

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

Chociaż prawdopodobnie powinieneś rozważyć odpowiedź Sixteen Otto.

Anthony Cramp
źródło
10

Właśnie przekazałem zero, aby zanegować ostrzeżenia, może to zadziała dla ciebie?

NSLog (myString, nil);

Martytoof
źródło
5
Czy ktoś może wyjaśnić DLACZEGO podanie zera jako drugiego paramente rozwiązuje ostrzeżenie?
cprcrack
1
Podanie wartości nil jest jawne, podczas gdy brak drugiego parametru nie. Możesz założyć, że Twój kominek nie był rozpalony, kiedy wychodziłeś z domu, lub możesz upewnić się, że nie był. Chociaż zwykle nic się nie dzieje, ponieważ rzadko używasz kominka, będzie to jeden raz, gdy twój dom się spali.
1
@SoldOutActivist Nieprzydatne. Nieoczywistym punktem tutaj (dla kogoś, kto nie pochodzi ze środowiska C) jest różnica w zachowaniu między przekazaniem wyraźnego zera a nieprzekazywaniem czegokolwiek, a twój komentarz tego nie wyjaśnia.
Mark Amery,
Dobra: metoda Anything Obj-C, która może akceptować zmienną liczbę argumentów, musi być jawnie zakończona zerem. Przekazanie niczego nie jest tym samym, co podanie zera. Spędź trochę czasu z Obj-C, a zobaczysz to w kółko. Tworzenie tablic jest najbardziej powszechne.
3
Może to zatrzymać ostrzeżenie kompilatora, ale podstawowy problem, który został wyjaśniony przez Jona Hessa , nadal istnieje - jeśli jest więcej niż jeden specyfikator formatu myString, pierwszy będzie w porządku, ale drugi usunie śmieci ze stosu. Lista podmiany w nigdy nieNSLog() jest zakończona, @Sold. Istnieją dwie możliwości ustalenia, jak długa jest lista argumentów: wartość wartownicza lub co jest używane w i rodzina - kolejny argument, który umożliwia obliczenie liczby (np. Przez zliczenie specyfikatorów formatu). nilprintf()
jscs,
3

Jeśli chcesz raz na zawsze pozbyć się ostrzeżenia „format nie jest literałem łańcuchowym i brakiem argumentów formatu”, możesz wyłączyć ustawienie ostrzeżenia GCC „Typecheck wywołuje printf / scanf” (GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO) w ustawieniach kompilacji celu.

aldi
źródło
5
Spowoduje to wyciszenie ostrzeżenia, ale nie naprawi podstawowej usterki w aplikacji. Wyciszając ostrzeżenie, ignorujesz potencjalny błąd, który może spowodować awarię aplikacji na podstawie danych wprowadzonych przez użytkownika (lub w tym przypadku komunikatu o błędzie wygenerowanego przez CoreData). Lepiej byłoby zastosować się do innych odpowiedzi w tym pytaniu, aby usunąć błąd w kodzie źródłowym, który powoduje pojawienie się ostrzeżenia.
Christopher Fairbairn,
2
To prawda ... Dlatego napisałem „pozbądź się ostrzeżenia” zamiast „rozwiąż”.
Aldi
Napotkałem przypadek, w którym biblioteka uthash wyzwalała to ostrzeżenie przy wywołaniach jej funkcji utstring_printf, więc jest to przydatne w sytuacjach, gdy ostrzeżenie jest błędne.
alfwatt
2

NSLog () oczekuje ciągu formatu, a przekazywany jest tylko ciąg. Nie musisz używać stringWithFormat :, możesz po prostu:

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

A to sprawi, że ostrzeżenie zniknie.

Elfred
źródło
2

FWIW, dotyczy to również programistów iPhone'a. Koduję względem zestawu SDK 3.1.3 i otrzymałem ten sam błąd z tym samym problemem (zagnieżdżenie stringWithFormat wewnątrz NSLog ()). Sixten i Jon szukają pieniędzy.

Pettiross
źródło
0

Po prostu poinformowanie kogokolwiek o użyciu appendFormaton NSMutableString może również spowodować wyświetlenie tego ostrzeżenia, jeśli próbujesz przekazać sformatowany ciąg w następujący sposób:

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

Aby uniknąć tego ostrzeżenia, zamień powyższe na to:

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

Bardziej zwięzłe i bezpieczniejsze. Cieszyć się!

ColossalChris
źródło
-2
NSLog(@"%@ %@, %@", 
       errorMsgFormat, 
       error, 
       [error userInfo]); 
ILYA2606
źródło
1
Używanie stringWithFormatjest tu zbędne, kiedy możesz to zrobićNSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])
Mark Amery,