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

После обновления до последней версии 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? И как это рекомендуется исправить, чтобы предупреждение не генерировалось?


person Alexi Groove    schedule 05.11.2009    source источник


Ответы (11)


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

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

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

NSLog(@"%@, %@", [NSString stringWithFormat:errorMsgFormat, error], 
   [error userInfo]);              
person Sixten Otto    schedule 05.11.2009
comment
Я не думаю, что NSLog () любит принимать только один аргумент NSLog() может принимать один аргумент, когда строка формата не содержит спецификаторов формата. - person user102008; 11.03.2011
comment
Выдает еще одно предупреждение. Аргумент данных не используется строкой формата. - person hasan; 19.02.2014

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]);
person Jon Hess    schedule 05.11.2009
comment
И снова на SO подробные и хорошие ответы остаются на второй план. СПАСИБО за подробные объяснения. Я бы никогда этого не понял. - person Dan Rosenstark; 27.02.2010

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

В таком случае правильное решение - это ИСПОЛЬЗОВАТЬ строку формата напрямую, например

NSLog(@"%@", myNSString);

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

person Alex Whittemore    schedule 18.08.2010

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

#pragma GCC diagnostic ignored "-Wformat-security"

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

РЕДАКТИРОВАТЬ: Начиная с clang, прагма изменилась. См. Это: https://stackoverflow.com/a/17322337/3937

person Qrikko    schedule 09.05.2011

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

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

Хотя вам, вероятно, стоит подумать об ответе Шестнадцати Отто.

person Anthony Cramp    schedule 05.11.2009

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

NSLog (myString, ноль);

person Martytoof    schedule 07.04.2010
comment
Может ли кто-нибудь объяснить, ПОЧЕМУ передача nil в качестве второго параметра решает предупреждение? - person cprcrack; 03.10.2012
comment
Передача nil является явной, а отсутствие второго параметра - нет. Вы можете предположить, что ваш камин не горел, когда вы выходили из дома, или можете убедиться, что это не так. Хотя обычно ничего не происходит из-за того, что вы редко пользуетесь камином, только однажды ваш дом сгорит. - person ; 11.03.2013
comment
@SoldOutActivist Бесполезно. Неочевидный момент здесь (для тех, кто не имеет опыта работы с C) - это разница в поведении между передачей явного nil и отсутствием передачи, и ваш комментарий не объясняет этого. - person Mark Amery; 02.07.2013
comment
Хорошо: все методы Obj-C, которые могут принимать переменное количество аргументов, должны явно оканчиваться нулем. Ничего не передать - это не то же самое, что передать ноль. Проведите любое время с Obj-C, и вы будете видеть это снова и снова. Строительные массивы являются наиболее распространенными. - person ; 03.07.2013
comment
Это может остановить предупреждение компилятора, но основная проблема, которая была объяснена Джоном Хессом, все еще существует - если их больше, чем один спецификатор формата в myString, первый подойдет, но второй будет собирать мусор из стека. Список замен в NSLog() никогда nil-завершается, @Sold. Есть два варианта определения длины списка аргументов: контрольное значение или то, что используется в printf() и семействе - еще один аргумент, который позволяет вычислить число (например, путем подсчета спецификаторов формата). - person jscs; 08.07.2013
comment
-1; как объясняет комментарий Джоша Касвелла, это неправильный способ заставить предупреждение исчезнуть и оставить основную проблему. - person Mark Amery; 23.10.2013

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

person aldi    schedule 19.11.2009
comment
Это отключит предупреждение, но ничего не сделает для исправления основной ошибки в вашем приложении. Выключая предупреждение, вы игнорируете потенциальную ошибку, которая может привести к сбою вашего приложения, основываясь только на данных, введенных пользователем (или в данном случае на сообщении об ошибке, созданном CoreData). Было бы лучше следовать некоторым другим ответам в этом вопросе, чтобы удалить ошибку в исходном коде, которая вызывает появление предупреждения. - person Christopher Fairbairn; 11.12.2009
comment
Верно ... Вот почему я написал избавиться от предупреждения вместо решения. - person aldi; 14.12.2009
comment
Я столкнулся со случаем, когда библиотека uthash запускала это предупреждение при вызове функции utstring_printf, поэтому это полезно в ситуациях, когда предупреждение неверно. - person alfwatt; 30.09.2013

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

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

И тогда предупреждение исчезнет.

person Elfred    schedule 05.11.2009

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

person Pettiross    schedule 19.05.2010

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

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

Чтобы избежать этого предупреждения, превратите приведенное выше в следующее:

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

Более кратко и надежно. Наслаждаться!

person ColossalChris    schedule 10.09.2015

person    schedule
comment
Использование stringWithFormat здесь излишне, если можно просто NSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo]) - person Mark Amery; 23.10.2013