Ios 目标-C:“;格式字符串不是字符串文字(可能不安全)";宏警告
我使用宏来简化返回本地化字符串的过程,如下所示:Ios 目标-C:“;格式字符串不是字符串文字(可能不安全)";宏警告,ios,objective-c,string,macros,Ios,Objective C,String,Macros,我使用宏来简化返回本地化字符串的过程,如下所示: #define GetLocalStr(key, ...) \ [NSString stringWithFormat:[[NSBundle mainBundle] localizedStringForKey:key value:@"" table:nil], ##__VA_ARGS__] #define GetLocalStrNoArgs(key) \ [[NSBundle mainBundle] localizedString
#define GetLocalStr(key, ...) \
[NSString stringWithFormat:[[NSBundle mainBundle] localizedStringForKey:key value:@"" table:nil], ##__VA_ARGS__]
#define GetLocalStrNoArgs(key) \
[[NSBundle mainBundle] localizedStringForKey:key value:@"" table:nil]
#define GetLocalStrArgs(key, ...) \
[NSString stringWithFormat:[[NSBundle mainBundle] localizedStringForKey:key value:@"" table:nil], ##__VA_ARGS__]
基本上,如果本地化字符串文件中有一个条目,如“name”=“My name is%@”代码>,正在呼叫
GetLocalStr( @"name", @"Foo" );
将返回NSString@“我的名字是Foo”
但是,当我运行它时,就像:
NSString * str = GetLocalStr( @"name", @"Foo" );
我得到了“格式字符串不是字符串文字”的警告。即使按照其他答案中关于此警告的建议,并将其替换为:
NSString * str = [NSString stringWithFormat:@"%@", GetLocalStr( @"name", @"Foo" )];
我仍然得到警告,而且,这有点违背了宏观经济让生活更轻松的观点
除了将所有GetLocalStr
调用包装在#pragma抑制器中之外,我如何摆脱警告
编辑27/08
在浏览了CRD的答案并做了更多的测试之后,我似乎对错误做出了错误的假设。澄清:
本地化字符串文件:
代码:
我的大多数翻译都没有任何论据,这些都是给出警告的,但直到我通读了CRD的答案,我才把它们联系起来
我将单个宏更改为两个,如下所示:
#define GetLocalStr(key, ...) \
[NSString stringWithFormat:[[NSBundle mainBundle] localizedStringForKey:key value:@"" table:nil], ##__VA_ARGS__]
#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(#u_uva_ARGS_uu)
来确定是否传递了任何参数-它将参数字符串化,如果大小为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
宏正确展开?Clang&GCC编译器检查格式字符串和提供的参数是否一致,如果格式字符串不是文本,则无法执行此操作-因此,当您从包中获取格式字符串时会看到错误消息
为了解决这个问题,有一个属性,format_arg(n)
(),用于标记采用格式字符串的函数;在不改变实际格式说明符的情况下以某种方式对其进行修改,例如翻译;然后把它还给我。Cocoa为此属性提供了方便的宏NS\u FORMAT\u ARG(n)
要解决问题,您需要做两件事:
将对NSBundle
的调用包装到指定了此属性的函数中;及
更改“键”以包含格式说明符
其次,字符串文件应包含:
"name %@" = "My name is %@"
因此,键具有与结果相同的格式说明符(如果需要对特定语言的说明符重新排序,则使用位置格式说明符)
现在定义一个简单的函数来执行查找,并将其指定为格式转换函数。注意:我们将其标记为静态内联
,使用宏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")
您将获得本地化格式字符串和格式检查
更新
在格雷格的评论之后,我回去检查了一下——我复制了你的错误,因此认为这是因为缺少了一个属性。然而,正如Greg指出的那样,localizedStringForKey:value:table:
已经具有该属性,那么为什么会出现错误呢?我心不在焉地再现了你的错误:
NSLog( GetLocalStr( @"name %@", @"Foo" ) );
编译器指向宏定义而不是那一行-我应该发现编译器误导了我
那你现在的处境如何?也许你也做过类似的事情?关键是格式字符串必须是文本或作为格式转换函数属性化的函数/方法的结果。别忘了,您的密钥还必须具有如上所述的格式说明符
更新2
在附加注释之后,您需要使用的是函数,而不是宏,以及format
属性,Cocoa为其提供了方便的NS\u format\u函数(f,a)
宏。此属性通知编译器该函数是格式化函数,f
的值是格式字符串的编号,a
是格式的第一个参数的编号。这给出了函数声明:
NSString *GetLocalStr(NSString *key, ...) NS_FORMAT_FUNCTION(1,2);
以及定义(假设为圆弧):
(本质上与@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_列表
):
结果:
我叫福
TestNoArgs
你好,福娃
它不能准确回答问题,但可能会帮助您在找到解决方案之前避免警告。@“name”=@“My name is%@”代码>-文字不可分配,无法编译<代码>NSString*格式=@“我的名字是%@”;NSString*str=GetLocalStr(格式为@“Foo”);NSLog(@“%@”,str)
不会抛出任何警告(我只在GetLocalStr
定义中预期),您可能希望修复引号并提供有关警告出现位置的更多详细信息。您可以创建一个NSString类别来为您执行此操作。@a-Live@“name”=@“My name is%@”
行位于字符串本地化文件中,所以从技术上讲,它应该是“name”=“My name is%@”-如果不清楚的话,很抱歉。警告标记在NSString*str=GetLocalStr(…)代码>line@JoshCaswell我知道关于这场战争的其他问题
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
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);