【问题标题】:Warning: "format not a string literal and no format arguments"警告:“格式不是字符串文字,也没有格式参数”
【发布时间】:2010-12-13 06:21:50
【问题描述】:

自从升级到最新的 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 调用有什么问题?以及建议的修复方法是什么,以便不生成警告?

【问题讨论】:

    标签: objective-c warnings string-formatting nslog


    【解决方案1】:

    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]);
    

    【讨论】:

    • 再一次在 SO 上,详细而好的答案被搁置了。感谢您充分解释这一点。我永远不会想到这一点。
    【解决方案2】:

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

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

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

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

    【讨论】:

    • “我不认为 N​​SLog() 喜欢只接受一个参数”NSLog() 可以接受一个参数,当格式字符串不包含格式说明符时。
    • 给出另一个警告数据参数没有被格式字符串使用。
    【解决方案3】:

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

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

    NSLog(@"%@", myNSString);
    

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

    【讨论】:

      【解决方案4】:

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

      #pragma GCC diagnostic ignored "-Wformat-security"

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

      编辑:从 clang 开始,编译指示发生了变化。看到这个:https://stackoverflow.com/a/17322337/3937

      【讨论】:

        【解决方案5】:

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

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

        不过,你或许应该考虑一下 16 奥托的回答。

        【讨论】:

          【解决方案6】:

          我刚刚传递了一个 nil 来否定警告,也许这对你有用?

          NSLog(myString, nil);

          【讨论】:

          • 有人可以解释为什么传递 nil 作为第二个参数来解决警告吗?
          • 传递 nil 是显式的,而缺少第二个参数则不是。你可以假设你离开房子时壁炉没有点燃,或者你可以确定它没有点燃。虽然通常什么都不会发生,因为您很少使用壁炉,但您的房子会被烧毁。
          • @SoldOutActivist 没有帮助。这里不明显的一点(对于不是来自 C 背景的人)是传递显式 nil 和不传递任何东西之间的行为差​​异,而您的评论没有解释这一点。
          • 很好:任何可以接受可变数量参数的 Obj-C 方法都必须显式地以 nil 终止。什么都不通过与通过零不一样。花任何时间使用 Obj-C,你会一遍又一遍地看到这一点。构建数组是最常见的。
          • 这可能会停止编译器警告,但潜在的问题,即explained by Jon Hess,仍然存在——如果myString 中有多个格式说明符,第一个会很好,但是第二个将从堆栈中拾取垃圾。 NSLog() 中的替换列表是 never nil-terminated, @Sold。有两个选项可以确定参数列表的长度:一个标记值,或者在printf() 和family 中使用的值——另一个允许计算数字的参数(例如,通过计算格式说明符)。跨度>
          【解决方案7】:

          如果您想一劳永逸地摆脱警告“格式不是字符串文字且没有格式参数”,您可以在目标的构建设置。

          【讨论】:

          • 这将使警告静音,但它不会做任何事情来修复您的应用程序中的潜在缺陷。通过使警告静音,您忽略了一个潜在的错误,该错误可能仅根据用户输入的数据(或在本例中为 CoreData 生成的错误消息)导致您的应用程序崩溃。最好遵循此问题中的其他一些答案,以删除导致出现警告的源代码中的错误。
          • 是的...这就是为什么我发布“摆脱警告”而不是“解决”的原因。
          • 我遇到了一个情况,即 uthash 库在调用它的 utstring_printf 函数时触发了这个警告,所以这在警告错误的情况下很有用。
          【解决方案8】:

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

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

          这将使警告消失。

          【讨论】:

            【解决方案9】:

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

            【讨论】:

              【解决方案10】:

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

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

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

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

              更简洁、更安全。享受吧!

              【讨论】:

                【解决方案11】:
                NSLog(@"%@ %@, %@", 
                       errorMsgFormat, 
                       error, 
                       [error userInfo]); 
                

                【讨论】:

                • 在这里使用 stringWithFormat 是多余的,而您可以只使用 NSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])
                猜你喜欢
                • 2011-05-24
                • 2015-01-27
                • 2013-04-07
                • 1970-01-01
                • 2014-12-23
                • 2011-08-12
                • 1970-01-01
                • 1970-01-01
                • 2013-06-20
                相关资源
                最近更新 更多