警告:“不格式化字符串文字,不格式化参数”


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电话吗?推荐的解决方法是什么,以便不生成警告?

Answers:


113

您是否正确嵌套了括号?我不NSLog()喜欢只接受一个论点,这就是您要通过的论点。另外,它已经为您完成了格式化。为什么不这样做呢?

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

或者,由于您说的errorMsgFormat是带有单个占位符的格式字符串,您是否要这样做?

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

14
NSLog()当格式字符串不包含格式说明符时,“我不认为NSLog()喜欢只接受一个参数” 可以接受一个参数。
user102008 2011年

给出另一个警告数据参数,格式字符串不使用该参数。
hasan 2014年

157

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:],...)。

正如Otto指出的那样,您可能应该做以下事情:

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

17
如此一来,详细而正确的答案就落到了一边。非常感谢您对此进行充分解释。我永远都不会想清楚这一点。
Dan Rosenstark 2010年

38

最终答案:正如Jon Hess所说,这是一个安全问题,因为您要将WHATEVER字符串传递给需要格式字符串的函数。也就是说,它将评估任何字符串内的所有格式说明符。如果没有,那太棒了,但是如果有,那么可能会发生坏事。

那么,正确的做法是直接使用格式字符串,例如

NSLog(@"%@", myNSString);

这样,即使myNSString中有格式说明符,它们也不会被NSLog评估。


13

我不特别建议使用此方法,因为警告是真正的警告。在动态使用该语言的情况下,可以对字符串进行运行(即插入新信息,甚至使程序崩溃)。如果您知道应该是这样,就强迫压制,而您真的不想被警告。

#pragma GCC diagnostic ignored "-Wformat-security"

会告诉GCC暂时忽略编译警告。同样,它不能解决任何问题,但是有时您可能找不到真正解决问题的好方法。

编辑:从c开始,杂注已更改。看到这个:https : //stackoverflow.com/a/17322337/3937


10

解决该问题的最快方法是将@"%@",第一个参数添加为NSLog调用,即

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

虽然,您可能应该考虑十六奥托的答案。


10

我刚刚通过了一个nil来消除警告,也许对您有用?

NSLog(myString,nil);


5
有人可以解释为什么第二个paramente解决警告时通过nil吗?
cprcrack 2012年

1
传递nil是显式的,而缺少第二个参数则不是。您可以假设离开家时壁炉没有点亮,或者可以确保没有点亮。虽然通常不会发生什么事,因为您很少使用壁炉,但这只是一次房屋被烧毁。

1
@SoldOutActivist无用。这里的一个非显而易见的观点(对于不是来自C背景的人)是行为上的区别是传递显式nil和不传递任何东西之间的区别,而您的评论没有对此进行解释。
Mark Amery 2013年

很好:任何可以接受可变数量参数的Obj-C方法都必须明确地用nil终止。不传递任何内容与传递nil不同。花任何时间在Obj-C上,您将一遍又一遍地看到它。构建数组是最常见的。

3
这可能会停止编译器警告,但是由Jon Hess解释的根本问题仍然存在-如果其中有多个格式说明符myString,则第一个会很好,但是第二个会从堆栈中获取垃圾。在替代清单,NSLog()永远不会 nil封端,@Sold。有两个选项可以确定参数列表的长度:前哨值,或in printf()和family中使用的参数–另一个参数,可以计算数字(例如,通过计数格式说明符)。
jscs

3

如果要彻底摆脱“格式不是字符串文字且没有格式参数”的警告,则可以在目标的构建设置中禁用GCC警告设置“对printf / scanf的Typecheck调用”(GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO)。


5
这将使警告消失,但不会做任何事情来修复应用程序中的潜在缺陷。通过消除警告,您可以忽略仅根据用户输入的数据(或在这种情况下为CoreData生成的错误消息)而可能导致应用程序崩溃的潜在错误。最好遵循此问题中的其他一些答案,以消除导致警告出现的源代码中的错误。
Christopher Fairbairn,2009年

2
是的,这就是为什么我发布“摆脱警告”而不是“解决”的原因。
阿尔迪

我遇到了一个情况:uthash库在调用它的utstring_printf函数时触发了此警告,因此在警告错误的情况下这很有用。
铝箔

2

NSLog()需要一个格式字符串,传入的只是一个字符串。您不需要使用stringWithFormat :,您可以执行以下操作:

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

这将使警告消失。


2

FWIW,这也适用于iPhone开发人员。我正在针对3.1.3 SDK进行编码,并遇到了相同的错误和相同的问题(在NSLog()中嵌套了stringWithFormat)。Sixten和Jon赚钱了。


0

appendFormat如果尝试传递如下格式的字符串,则仅让任何人知道使用on NSMutableString也会导致出现此警告:

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])
Mark Amery 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.