Objective-C: «строка формата не является строковым литералом (потенциально небезопасным)» предупреждение с макросом

Я использую макрос, чтобы упростить возврат локализованных строк, например:

#define GetLocalStr(key, ...) \ [NSString stringWithFormat:[[NSBundle mainBundle] localizedStringForKey:key value:@"" table:nil], ##__VA_ARGS__] 

В принципе, если у вас есть запись в файле Strings локализации, например "name" = "My name is %@"; , вызывая

 GetLocalStr( @"name", @"Foo" ); 

вернет NSString @"My name is Foo"

Когда я запускаю его, например:

 NSString * str = GetLocalStr( @"name", @"Foo" ); 

Я получаю предупреждение «string string is not string literal». Даже следуя советам других ответов на SO об этом предупреждении и заменяя его:

 NSString * str = [NSString stringWithFormat:@"%@", GetLocalStr( @"name", @"Foo" )]; 

Я все еще получаю предупреждение, и, кроме того, это похоже на то, что макрос делает жизнь проще.

Как я могу избавиться от предупреждения, которое не обертывает все вызовы GetLocalStr в #pragma suppressors?

Редактировать 27/08

Пройдя через CRD-ответ и выполняя еще несколько тестов, кажется, что я сделал плохое предположение об ошибке. Чтобы уточнить:

Файл локализации строк:

 "TestNoArgs" = "Hello world"; "TestArgs" = "Hello world %@"; 

Код:

 NSString * str1 = GetLocalStr( @"TestNoArgs" ); // gives warning NSString * str2 = GetLocalStr( @"TestArgs", @"Foo" ); // doesn't give warning 

Большинство моих переводов не принимают никаких аргументов, и это те, которые дают предупреждение, но я не подключался, пока не прочитал ответ CRD.

Я изменил свой единственный макрос на два, вот так:

 #define GetLocalStrNoArgs(key) \ [[NSBundle mainBundle] localizedStringForKey:key value:@"" table:nil] #define GetLocalStrArgs(key, ...) \ [NSString stringWithFormat:[[NSBundle mainBundle] localizedStringForKey:key value:@"" table:nil], ##__VA_ARGS__] 

И если я позвоню каждому отдельно, никаких предупреждений не будет.

Я бы хотел, чтобы GetLocalStr расширился до GetLocalStrNoArgs или GetLocalStrArgs зависимости от того, были ли переданы какие-либо аргументы или нет, но до сих пор мне не повезло (макросы не мой сильный костюм: D).

Я использую sizeof(#__VA_ARGS__) чтобы определить, есть ли какие-либо аргументы – это stringifys аргументы, и если размер равен 1, он пуст (т.е. `\ 0 '). Возможно, это не самый идеальный метод, но, похоже, он работает.

Если я переписал свой макрос GetLocalStr для:

 #define GetLocalStr(key,...) (sizeof(#__VA_ARGS__) == 1) ? GetLocalStrNoArgs(key) : GetLocalStrArgs(key,##__VA_ARGS__) 

Я могу использовать его, но я все равно получаю предупреждения везде, где он используется, и никаких аргументов не передается, а что-то вроде

 #define GetLocalStr( key,...) \ #if ( sizeof(#__VA_ARGS__) == 1 ) \ GetLocalStrNoArgs(key) \ #else \ GetLocalStrArgs(key,##__VA_ARGS__) 

не будет компилироваться. Как я могу получить макрос GetLocalStr для правильного расширения?

2 Solutions collect form web for “Objective-C: «строка формата не является строковым литералом (потенциально небезопасным)» предупреждение с макросом”

Компиляторы Clang & GCC проверяют, соответствуют ли строки формата и предоставленные аргументы, они не могут этого сделать, если строка формата не является литералом – следовательно, сообщение об ошибке вы видите, когда вы получаете строку формата из пакета.

Для решения этой проблемы существует атрибут format_arg(n) ( docs ) для обозначения функций, которые принимают строку формата; изменить его каким-либо образом, не изменяя конкретные спецификаторы формата, например, перевести его; а затем вернуть его. Какао предоставляет удобный макрос NS_FORMAT_ARG(n) для этого атрибута.

Чтобы исправить вашу проблему, вам нужно сделать две вещи:

  1. NSBundle вызов NSBundle в функции с указанным атрибутом; а также

  2. Измените свой «ключ», чтобы включить спецификаторы формата.

Во-вторых, файл ваших строк должен содержать:

 "name %@" = "My name is %@" 

поэтому ключ имеет такие же спецификаторы формата, как и результат (если вам нужно изменить порядок спецификаций для определенного языка, в котором используются спецификаторы позиционного формата).

Теперь определите простую функцию для поиска, приписывая ее как функцию преобразования формата. Обратите внимание, что мы NS_INLINE его как static inline , используя макрос NS_INLINE как подсказку компилятору, чтобы как встроить его в ваше расширение макроса; static позволяет включить его в несколько файлов без конфликтов символов:

 NS_INLINE NSString *localize(NSString *string) NS_FORMAT_ARGUMENT(1); NSString *localize(NSString *string) { return [[NSBundle mainBundle] localizedStringForKey:string value:@"" table:nil]; } 

И ваш макрос становится:

 #define GetLocalStr(key, ...) [NSString stringWithFormat:localize(key), ##__VA_ARGS__] 

Теперь, когда вы:

 GetLocalStr(@"name %@", @"Foo") 

Вы получите как локализованную строку формата, так и формат.

Обновить

После комментария Грега я вернулся и проверил – я воспроизвел вашу ошибку и предположил, что это был недостающий атрибут. Однако, как указывает Грег, localizedStringForKey:value:table: уже имеет атрибут, поэтому почему ошибка? То, что я рассеянно сделал при воспроизведении вашей ошибки, был:

 NSLog( GetLocalStr( @"name %@", @"Foo" ) ); 

и компилятор указал на определение макроса, а не на эту строку – я должен был заметить, что компилятор меня вводил в заблуждение.

Так где же это тебя покидает? Может быть, вы сделали что-то подобное? Ключ состоит в том, что строка формата должна быть либо литералом, либо результатом функции / метода, отнесенной как функция перевода текста. И не забывайте, что вы также должны иметь спецификатор формата для вашего ключа, как указано выше.

Обновление 2

После ваших дополнительных комментариев то, что вам нужно использовать, – это функция, а не макрос, а также атрибут format , для которого Cocoa предоставляет удобный NS_FORMAT_FUNCTION(f,a) макрос. Этот атрибут сообщает компилятору, что функция является форматирующей, значение f – это номер строки формата, а a – номер первого аргумента в формате. Это дает объявление функции:

 NSString *GetLocalStr(NSString *key, ...) NS_FORMAT_FUNCTION(1,2); 

и определение (предполагая ARC):

 NSString *GetLocalStr(NSString *key, ...) { va_list args; va_start(args, key); NSString *format = [[NSBundle mainBundle] localizedStringForKey:key value:@"" table:nil]; NSString *result = [[NSString alloc] initWithFormat:format arguments:args]; va_end (args); return result; } 

(что по сути то же самое, что и @ A-Live).

Использование этого будет проверено соответствующим образом, например:

 int x; ... NSString *s1 = GetLocalStr(@"name = %d", x); // OK NSString *s2 = GetLocalStr(@"name = %d"); // compile warning - More '%" conversions than data arguments NSString *s3 = GetLocalStr(@"name", x); // compile warning - Data argument not used by format string NSString *s4 = GetLocalStr(@"name"); // OK 

Этот вариант не дает никаких предупреждений (так как всегда есть va_list ):

 NSString* GetLocalStr1(NSString *formatKey, ...) { va_list ap; va_start(ap, formatKey); NSString * format = [[NSBundle mainBundle] localizedStringForKey:formatKey value:@"" table:nil]; NSString *result = [[NSString alloc] initWithFormat:format arguments:ap]; va_end (ap); return [result autorelease]; } ... __unused NSString * str = GetLocalStr1( @"name", @"Foo" ); __unused NSString * str1 = GetLocalStr1( @"TestNoArgs" ); __unused NSString * str2 = GetLocalStr1( @"TestArgs", @"Foo" ); NSLog(@"%@", str); NSLog(@"%@", str1); NSLog(@"%@", str2); 

Результат:

меня зовут Foo

TestNoArgs

Привет мир Foo

Он не отвечает на вопрос точно, но может помочь вам избежать предупреждений, пока решение не будет найдено.

  • Цель C: Удаление атрибутов HTML из строки
  • Название кнопки сравнения iOS для строки
  • Создание строки JSON из NSDictionary в iOS
  • Swift - какие типы использовать? NSString или String
  • Команда xcodebuild с абсолютным путем к SDK
  • Как я могу подчеркнуть текст в iOS 6?
  • Swift Как рассчитать высоту текста одной строки из ее шрифта
  • поиск без регистра - iphone
  • Преобразование экранированных символов UTF8 в исходную форму
  • Как разместить символ между UITextField
  • Как получить UITableView Label Text string - Пользовательская ячейка
  • Interesting Posts

    iOS BLE Bluetooth – отправка / получение данных HEX

    Регулировка положения вместе с interactivePopGestureRecognizer

    Открывать UIDocument синхронно

    Динамически удалить нулевое значение из словаря swift, используя функцию

    Глядя высокого разрешения GameCenter Иконка

    Как преобразовать изображение в массив шестнадцатеричных десятичных байтов, чтобы отправить его в выходной поток в iOS sdk

    как выполнить segue

    Добавление компонента year в исламский календарь не выполняется

    iOS setContentOffset не работает на ipad

    ABMultiValueCopyLabelAtIndex возвращает null, но метка видна в адресной книге

    Набор Sprite, удалите анимированные текстуры из памяти

    Быстрый режим фона для BLE iOS9

    Сертификат удостоверения подлинности SSL для запуска HTTPS-сервера на iOS

    UITextField, текст немного сбивается, когда начинается редактирование

    Как добавить UIView на панель навигации?

    PhoneC: Разработка iOS проста с помощью XCode, Swift3, UITableView, cocatouch, давайте создадим приложения для iPhone, iPad и Macbook.