110

自从升级到最新的 Xcode 3.2.1 和 Snow Leopard,我一直收到警告

“格式不是字符串文字,也没有格式参数”

从以下代码:

NSError *error = nil;

if (![self.managedObjectContext save:&error]) 
{
    NSLog([NSString stringWithFormat:@"%@ %@, %@", 
       errorMsgFormat, 
       error, 
       [error userInfo]]);      

}

如果errorMsgFormatNSString带有格式说明符(例如"print me like this: %@":),上述NSLog调用有什么问题?什么是修复它的推荐方法是不生成警告?

4

11 回答 11

158

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]);
于 2009-11-05T04:16:06.820 回答
113

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

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

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

NSLog(@"%@, %@", [NSString stringWithFormat:errorMsgFormat, error], 
   [error userInfo]);              
于 2009-11-05T01:54:13.627 回答
38

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

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

NSLog(@"%@", myNSString);

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

于 2010-08-18T00:01:43.920 回答
13

我不特别推荐使用这个,因为警告是一个真正的警告..在动态使用语言时,可以对字符串执行运行时操作(即插入新信息甚至使程序崩溃)..但是有可能如果你知道它应该是这样的并且你真的不想被警告它,就强制压制..

#pragma GCC diagnostic ignored "-Wformat-security"

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

编辑:截至clang,编译指示已经改变。看到这个:https ://stackoverflow.com/a/17322337/3937

于 2011-05-09T20:28:42.270 回答
10

解决它的最快方法是将@"%@",作为第一个参数添加到您的NSLog调用中,即

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

不过,您可能应该考虑 16 奥托的回答。

于 2009-11-05T04:01:13.137 回答
10

我刚刚通过 nil 来否定警告,也许这对你有用?

NSLog(myString, nil);

于 2010-04-07T16:48:58.283 回答
3

如果您想一劳永逸地摆脱“格式不是字符串文字和没有格式参数”的警告,您可以在目标的构建设置中禁用 GCC 警告设置“Typecheck Calls to printf/scanf”(GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO)。

于 2009-11-19T15:57:28.593 回答
2

NSLog() 需要一个格式字符串,传入的只是一个字符串。您不需要使用 stringWithFormat:,您可以这样做:

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

这将使警告消失。

于 2009-11-05T04:06:00.820 回答
2

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

于 2010-05-19T17:47:00.720 回答
0

如果尝试像这样传递格式化字符串,只是让任何人知道使用appendFormaton NSMutableString 也会导致出现此警告:

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

所以为了避免这个警告,把上面的变成这样:

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

更简洁,更安全。享受!

于 2015-09-10T21:15:49.213 回答
-2
NSLog(@"%@ %@, %@", 
       errorMsgFormat, 
       error, 
       [error userInfo]); 
于 2013-03-15T08:05:04.210 回答