Предупреждение: "формат не строковый литерал и аргументы формата"

Начиная с обновления до последнего Xcode 3.2.1 и Snow Leopard, я получаю предупреждение

"не строковый литерал и аргументы формата

из следующего кода:

NSError *error = nil;

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

}

Если errorMsgFormat является NSString с спецификаторами формата (например: "print me like this: %@"), что не так с вышеуказанным вызовом NSLog? И какой рекомендуемый способ исправить это, чтобы предупреждение не генерировалось?

Ответ 1

Правильно ли вы вложите свои скобки? Я не думаю, что NSLog() любит принимать только один аргумент, а именно то, что вы его передаете. Кроме того, он уже делает форматирование для вас. Почему бы просто не сделать это?

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

Или, поскольку вы говорите, что errorMsgFormat является строкой формата с одним заполнителем, пытаетесь ли вы это сделать?

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

Ответ 2

Xcode жалуется, потому что это проблема безопасности.

Здесь код похож на ваш:

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

Эта последняя инструкция NSLog будет выполнять эквивалент этого:

NSLog(@"Jon Hess %@");

Это заставит NSLog искать еще один строковый аргумент, но его нет. Из-за того, как работает язык C, он собирает некоторый случайный указатель мусора из стека и пытается рассматривать его как NSString. Это скорее всего приведет к краху вашей программы. Теперь ваши строки, вероятно, не имеют% @в них, но в какой-то момент они могут. Вы всегда должны использовать строку формата с данными, которые вы явно контролируете как первый аргумент для функций, которые принимают строки форматирования (printf, scanf, NSLog, - [NSString stringWithFormat:],...).

Как указывает Отто, вы должны просто сделать что-то вроде:

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

Ответ 3

Окончательный ответ: как сказал Джон Хесс, это проблема безопасности, потому что вы передаете строку WHATEVER в функцию, ожидающую строку формата. То есть, он будет оценивать все спецификаторы формата WITHIN любой строки. Если их нет, это удивительно, но если есть, могут случиться плохие вещи.

Собственно, что нужно сделать, это USE строку формата напрямую, например

NSLog(@"%@", myNSString);

Таким образом, даже если в myNSString есть спецификаторы формата, они не получают оценку NSLog.

Ответ 4

Я не рекомендую это использовать, так как предупреждение является реальным предупреждением.. при динамическом использовании языка он может делать что-то исполняемое в строке (т.е. вставлять новую информацию или даже сбой программы). Однако возможно заставить подавить, если вы ЗНАЕТЕ, что это должно быть так, и вы действительно не хотите, чтобы вас предупреждали об этом.

#pragma GCC diagnostic ignored "-Wformat-security"

Скажет GCC временно игнорировать предупреждение компиляции. Опять же, это не решение ничего, но могут быть моменты, когда вы не можете найти хороший способ исправить проблему.

РЕДАКТИРОВАТЬ: По состоянию клана прагма изменилась. Смотрите это: fooobar.com/questions/54402/...

Ответ 5

Самый быстрый способ исправить это - добавить @"%@", в качестве первого аргумента для вашего вызова NSLog, т.е.

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

Хотя, вы, вероятно, должны рассмотреть ответ Шестнадцати Отто.

Ответ 6

Я только что пропустил ник, чтобы отрицать предупреждения, может быть, это сработает для вас?

NSLog (myString, nil);

Ответ 7

NSLog() ожидает строку формата, то, что передается, является просто строкой. Вам не нужно использовать stringWithFormat:, вы можете просто сделать:

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

И это заставит предупреждение уйти.

Ответ 8

Если вы хотите избавиться от предупреждения "формат, а не строковый литерал и аргументы формата" раз и навсегда, вы можете отключить параметр предупреждения GCC "Typecheck Calls to printf/scanf" (GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO) в вашей целевой строить настройки.

Ответ 9

FWIW, это относится и к iPhone dev. Я кодируюсь с SDK 3.1.3 и получаю ту же ошибку с той же проблемой (вставка stringWithFormat внутри NSLog()). Сикстен и Джон на деньги.

Ответ 10

Просто сообщить кому-нибудь об использовании appendFormat в NSMutableString также может вызвать это предупреждение, если вы пытаетесь передать в форматированной строке, например:

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

Итак, чтобы избежать этого предупреждения, включите это:

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

Более сжатый и более безопасный. Наслаждайтесь!

Ответ 11

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