在生产代码上不应该使用 NSLog() ,这是真的吗?

我在这个网站上被告知过几次,但是我想确定这是真的。

我希望能够在我的代码中添加 NSLog 函数调用,而 Xcode/gcc 在构建我的发布/分发版本时会自动删除这些调用。

我应该避免使用这种方法吗? 如果应该,有经验的 Objective-C 程序员之间最常用的替代方法是什么?

33362 次浏览

NSLog 调用可以保留在生产代码中,但是只有在真正异常的情况下,或者需要记录到系统日志中的信息时,才应该保留 NSLog 调用。

丢弃系统日志的应用程序令人讨厌,而且给人的印象是不专业的。

编辑: 由 Marc Charbonneau发布的 方法,由 阿修引起我的注意,比这个要好得多。

我已经删除了我的答案中建议在禁用调试模式时使用空函数禁用日志记录的部分。处理设置自动预处理器宏的部分仍然是相关的,所以它仍然存在。我还编辑了预处理器宏的名称,以便更好地适应 Marc Charbonneau 的答案。


为了在 Xcode 实现自动(和预期的)行为:

在项目设置中,转到“ Build”选项卡,并选择“ Debug”配置。找到“预处理宏”部分,并添加一个名为 DEBUG_MODE的宏。

...

编辑: 请参阅 Marc Charbonneau 的回答了解使用 DEBUG_MODE宏启用和禁用日志记录的正确方法。

我同意马修。NSLog 在产品代码中没有任何问题。事实上,它对用户是有用的。也就是说,如果您使用 NSLog 的唯一原因是为了帮助调试,那么,是的,应该在发布之前删除它。

此外,由于您已经将这个问题标记为 iPhone 问题,NSLog 需要资源,而 iPhone 很少拥有这些资源。如果你在 iPhone 上使用 NSLogging 什么都行,这会占用你的应用程序的处理器时间。好好利用。

预处理器宏确实非常适合调试。NSLog ()没有什么问题,但是用更好的功能定义您自己的日志功能很简单。下面是我使用的一个,它包含文件名和行号,以便于跟踪日志语句。

#define DEBUG_MODE


#ifdef DEBUG_MODE
#define DebugLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define DebugLog( s, ... )
#endif

我发现将整个语句放在前缀头中比放在它自己的文件中更容易。如果愿意,您可以通过使 DebugLog 与普通 Objective-C 对象交互来构建一个更复杂的日志系统。例如,您可以有一个日志类,它写入自己的日志文件(或数据库) ,并包含一个可以在运行时设置的“优先级”参数,因此调试消息不会显示在您的发行版中,但错误消息会显示(如果您这样做,您可以使 DebugLog () ,WarningLog () ,等等)。

哦,请记住,#define DEBUG_MODE可以在应用程序的不同位置重用。例如,在我的应用程序中,我使用它来禁用许可证密钥检查,只允许应用程序在特定日期之前运行。这使我能够以最少的努力发布一个有时间限制的、功能齐全的 beta 版本。

我不能对 Marc Charbonneau 的回答发表评论,所以我会把它作为一个答案。

除了将宏添加到预编译头之外,还可以使用 Target 构建配置来控制定义(或缺乏定义) DEBUG_MODE

如果您选择“ 调试”活动配置,将定义 DEBUG_MODE,宏将扩展到完整的 NSLog定义。

选择“ 放手”活动配置将不会定义 DEBUG_MODE,您的 NSLogging 将从版本构建中省略。

步骤:

  • 目标 > 索取资料
  • 建立标签
  • 搜索“ PreProcessor Macros”(或 GCC_PREPROCESSOR_DEFINITIONS)
  • 选择 Configuration: 调试
  • 编辑此级别的定义
  • 加入 DEBUG_MODE=1
  • 选择 Configuration: 放手
  • 确认 GCC_PREPROCESSOR_DEFINITIONS中没有设置 DEBUG_MODE

如果在定义中省略了’=’字符,就会从预处理器得到一个错误

此外,将此注释(如下所示)粘贴到宏定义上方,以提醒您 DEBUG_MACRO定义的来源;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1

我强烈推荐使用 TestFlight 进行日志记录(免费)。他们的方法将覆盖 NSLog (使用一个宏) ,并允许您为所有现有的 NSLog 调用打开/关闭对他们的服务器、 Apple System 日志和 STDERR 日志的日志记录。这样做的好处是,您仍然可以查看部署到测试人员的应用程序的日志消息,以及部署在应用程序商店中的应用程序的日志消息,而无需将日志显示在用户的系统日志中。两全其美。

正如在其他答案中指出的那样,您可以使用 # Definition 来更改是否在编译时使用 NSLog。

然而,更灵活的方法是使用像 可可伐木工这样的日志库,它允许您更改是否在运行时也记录了某些内容。

在代码中用 DDLogVerbose 或 DDLogError 等替换 NSLog,为宏定义等添加 # import 并设置日志记录器,通常使用 applicationDidFinishLaunching 方法。

要具有与 NSLog 相同的效果,配置代码是

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];

您不应该在发布代码中对 printf 或 NSLog 进行不必要的冗长处理。如果应用程序发生了不好的事情,例如一个不可恢复的错误,那么只尝试执行 printf 或 NSLog。

从安全性的角度来看,这取决于所记录的内容。如果 NSLog(或其他日志记录器)正在写入敏感信息,那么您应该删除生产代码中的日志记录器。

从审计的角度来看,审计人员不希望查看 NSLog的每一次使用,以确保其不记录敏感信息。他/她将简单地告诉您删除日志记录器。

我和两个组都有合作。我们审核代码,编写代码指南等等。我们的指南要求在生产代码中禁用日志记录。所以内部团队知道不要尝试;)

我们还将拒绝登录到生产环境中的外部应用程序,因为我们不想接受与意外泄露敏感信息相关的风险。我们不在乎开发商告诉我们什么。根本不值得我们花时间去调查。

请记住,我们定义的是“敏感”,而不是开发者;)

我还注意到一个应用程序执行了大量的日志作为一个应用程序准备内爆。执行/需要这么多日志记录是有原因的,而且它通常不稳定。它与重新启动挂起服务的“看门狗”线程一样。

如果您从未经历过安全体系结构(SecArch)评审,那么这些就是我们要研究的内容。

把这3行放在-prefix.pch 文件的末尾:

#ifndef DEBUG
#define NSLog(...) /* suppress NSLog when in release mode */
#endif

您不需要在项目中定义任何内容,因为在创建项目时,默认情况下 DEBUG是在生成设置中定义的。

一个简单的事实是,NSLog 的速度实在太慢了。

但是为什么呢? 为了回答这个问题,让我们看看 NSLog 是做什么的,以及它是如何做到的。

NSLog 到底是做什么的?

NSLog 做两件事:

它将日志消息写入 Apple System Logging (asl)工具,这样日志消息就可以显示在 Console.app 中。 它还检查应用程序的 stderr 流是否要到达终端(例如当应用程序通过 Xcode 运行时)。如果是这样,它将日志消息写入 stderr (以便在 Xcode 控制台中显示)。

给 STDERR 写信听起来并不困难。这可以通过 fprintf 和 stderr 文件描述符引用来实现。那 Asl 呢?

我找到的关于美国手语的最好的文档是 Peter Hosey: 链接的一篇10部分的博客文章

不需要太多细节,重点(因为它关系到表现)是:

要向 ASL 工具发送日志消息,基本上需要打开到 ASL 守护进程的客户端连接并发送消息。但是-每个线程必须使用一个单独的客户端连接。因此,为了线程安全,每次调用 NSLog 时,它都会打开一个新的 asl 客户端连接,发送消息,然后关闭连接。

资源可以找到 给你给你

请记住,NSLogs 可能会降低 UI/主线程的速度。除非绝对必要,否则最好从版本构建中删除它们。