Попередження: "формат не є рядковим буквальним і не аргументом формату"


110

З моменту оновлення до останнього 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викликом? І який рекомендований спосіб виправити так, щоб попередження не генерувалося?

Відповіді:


113

Ви правильно вкладаєте дужки? Я не думаю, що NSLog()подобається брати лише один аргумент, саме це ти передаєш. Крім того, це вже робить форматування для вас. Чому б просто не зробити цього?

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

Або, оскільки ви говорите errorMsgFormat, що це рядок формату з одним заповнювачем, ви намагаєтесь це зробити?

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

14
"Я не думаю, що NSLog () любить брати лише один аргумент" NSLog()може приймати один аргумент, коли рядок формату не містить специфікаторів формату.
користувач102008

Подає ще одне попередження Аргумент даних, який не використовується рядком формату.
хасан

157

Xcode скаржиться, оскільки це проблема безпеки.

Ось код, подібний до вашого:

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

Цей останній оператор NSLog буде виконувати еквівалент цього:

NSLog(@"Jon Hess %@");

Це змусить NSLog шукати ще один рядковий аргумент, але його немає. Через те, як працює мова С, він збирається зібрати якийсь випадковий вказівник сміття зі стека і спробувати ставитися до нього як до NSString. Це, швидше за все, призведе до збою вашої програми. Зараз у ваших струнах, мабуть, немає% @ в них, але вони можуть бути в якийсь день. Ви завжди повинні використовувати форматний рядок із даними, якими ви явно керуєте, як перший аргумент для функцій, які приймають рядки формату (printf, scanf, NSLog, - [NSString stringWithFormat:], ...).

Як вказує Отто, вам, мабуть, варто просто зробити щось на кшталт:

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

17
І ще раз на SO, детальні та гарні відповіді потрапляють убік. ДЯКУЙТЕ за все, що пояснили це. Я б ніколи цього не з'ясував.
Дан Розенстарк

38

Остаточна відповідь: Як сказав Джон Хесс, це проблема безпеки, оскільки ви передаєте рядок WHATEVER до функції, що очікує рядка формату. Тобто він оцінить усі специфікатори формату ВІД будь-якого рядка. Якщо таких немає, дивовижно, але якщо є, то погані речі можуть трапитися.

Отже, наприклад, використовувати USE рядка формату безпосередньо

NSLog(@"%@", myNSString);

Таким чином, навіть якщо в myNSString є специфікатори формату, вони не оцінюються NSLog.


13

Я особливо не рекомендую використовувати це, оскільки попередження є справжнім попередженням .. при динамічному використанні мови можна виконувати речі під час виконання рядка (тобто вставляти нову інформацію або навіть збоїти програму). Однак це можливо примушувати придушувати, якщо ви знаєте, що це має бути так, і ви насправді не хочете, щоб про це попередили ..

#pragma GCC diagnostic ignored "-Wformat-security"

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

EDIT: Станом на кланг, прагма змінилася. Дивіться це: https://stackoverflow.com/a/17322337/3937


10

Найшвидший спосіб виправити це було б додати @"%@",як перший аргумент до вашого NSLogдзвінка, тобто

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

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


10

Я щойно пройшов нуль, щоб зняти попередження, можливо, це спрацювало б для вас?

NSLog (myString, nil);


5
Чи може хтось пояснити ЧОМУ проходження нуля, коли другий парамент вирішує попередження?
cprcrack

1
Пропуск нуля явний, тоді як відсутність другого параметра не є. Ви можете припустити, що ваш камін не запалився, коли виходили з дому, або ви можете переконатися, що його не було. Хоча зазвичай нічого не відбувається, тому що ви рідко використовуєте свій камін, це буде саме той час, коли ваш будинок згорить.

1
@SoldOutActivist Unhelpful. Тут неочевидний момент (для когось, хто не походить зі С) - це різниця в поведінці між проходженням явного нуля і нічого не пройти, і ваш коментар цього не пояснює.
Марк Амері

Добре: Будь-який метод Obj-C, який може прийняти змінну кількість аргументів, повинен бути явно скасований на нуль. Пройти нічого - це не те, що пройти нуль. Проведіть будь-який час з Obj-C, і ви побачите це знову і знову. Будівництво масивів є найпоширенішим.

3
Це може зупинити попередження компілятора, але основна проблема, яку пояснив Джон Хесс , все ще існує - якщо в ньому більше одного специфікатора формату myString, перший буде добре, а другий вибере сміття зі стека. Список підміни NSLog()НЕ ніколи nil -завершённий, @Sold. Існує два варіанти з'ясування тривалості списку аргументів: значення дозорного або те, що використовується в printf()сім'ї - ще один аргумент, що дозволяє обчислити число (наприклад, шляхом підрахунку специфікаторів формату).
jscs

3

Якщо ви хочете позбутися попередження "формат не рядковий буквальний і не має аргументів формату" раз і назавжди, ви можете відключити налаштування попередження GCC "Набір перевірки викликів до printf / scanf" (GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = НІ) у налаштуваннях складання вашої цілі.


5
Це заглушить попередження, але воно не зробить нічого, щоб виправити основний недолік у вашій програмі. Замовчуючи попередження, ви ігноруєте потенційну помилку, яка може зламати вашу програму на основі просто даних, введених користувачем (або в цьому випадку повідомлення про помилку, створене CoreData). Було б краще дотримуватися деяких інших відповідей у ​​цьому питанні, щоб видалити помилку у вихідному коді, що спричиняє появу попередження.
Крістофер Фербайрн

2
Правда ... Ось чому я розмістив "позбутися попередження" замість "вирішити".
альді

Я наткнувся на випадок, коли бібліотека uthash викликала це попередження при викликах до його функції utstring_printf, тому це корисно в ситуаціях, коли попередження неправильне.
alfwatt

2

NSLog () очікує рядок формату, те, що передається, - це лише рядок. Вам не потрібно використовувати stringWithFormat :, ви можете просто зробити:

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

І це призвело б до попередження.


2

FWIW, це стосується і iPhone Dev. Я кодую проти 3.1.3 SDK і отримав ту саму помилку з тією ж проблемою (вкладення stringWithFormat всередині NSLog ()). Шістен і Джон на гроші.


0

Просто даючи кому-небудь знати про використання appendFormatNSMutableString також може призвести до появи цього попередження при спробі передачі у форматованому рядку так:

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

Щоб уникнути цього попередження, перетворіть вищезазначене на це:

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

Більш лаконічний і безпечніший. Насолоджуйтесь!


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

1
Використання stringWithFormatтут зайве, коли можна було просто зробитиNSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])
Марк Амеррі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.