我在这个网站上被告知过几次,但我想确保情况确实如此。
我希望能够在我的代码中散布 NSLog 函数调用,并且 Xcode/gcc 会在构建我的发布/分发版本时自动删除这些调用。
我应该避免使用这个吗?如果是这样,有经验的 Objective-C 程序员之间最常见的选择是什么?
我在这个网站上被告知过几次,但我想确保情况确实如此。
我希望能够在我的代码中散布 NSLog 函数调用,并且 Xcode/gcc 会在构建我的发布/分发版本时自动删除这些调用。
我应该避免使用这个吗?如果是这样,有经验的 Objective-C 程序员之间最常见的选择是什么?
预处理器宏确实非常适合调试。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 副本。
将这 3 行放在 -prefix.pch 文件的末尾:
#ifndef DEBUG
#define NSLog(...) /* suppress NSLog when in release mode */
#endif
您不需要在项目中定义任何内容,因为DEBUG
在创建项目时默认在构建设置中定义。
NSLog 调用可以留在生产代码中,但应该只用于真正的例外情况,或者需要记录到系统日志中的信息。
乱扔系统日志的应用程序很烦人,而且不专业。
我无法对Marc Charbonneau 的回答发表评论,所以我会将其发布为答案。
除了将宏添加到预编译的标头之外,您还可以使用 Target 构建配置来控制DEBUG_MODE
.
如果您选择“调试”活动配置,DEBUG_MODE
将被定义,并且宏扩展为完整NSLog
定义。
选择“发布”活动配置将不会定义DEBUG_MODE
,并且您的NSLog
ging 将从发布构建中省略。
脚步:
GCC_PREPROCESSOR_DEFINITIONS
)DEBUG_MODE=1
DEBUG_MODE
未设置GCC_PREPROCESSOR_DEFINITIONS
如果你在定义中省略了'='字符,你会从预处理器中得到一个错误
此外,在宏定义上方粘贴此注释(如下所示)以提醒您DEBUG_MACRO
定义的来源;)
// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
// = Debug: DEBUG_MODE=1
编辑:由Marc Charbonneau发布并由sho引起我注意的方法远比这个好。
我已经删除了我的答案部分,该部分建议在禁用调试模式时使用空函数来禁用日志记录。处理设置自动预处理器宏的部分仍然是相关的,所以它仍然存在。我还编辑了预处理器宏的名称,使其更适合 Marc Charbonneau 的回答。
要在 Xcode 中实现自动(和预期)行为:
在项目设置中,转到“构建”选项卡,然后选择“调试”配置。找到“预处理器宏”部分,并添加一个名为DEBUG_MODE
.
...
编辑:有关使用宏启用和禁用日志记录的正确方法,请参阅Marc Charbonneau 的答案。DEBUG_MODE
我同意马修的观点。NSLog 在生产代码中没有任何问题。事实上,它对用户很有用。也就是说,如果您使用 NSLog 的唯一原因是帮助调试,那么,是的,应该在发布之前将其删除。
此外,由于您已将此标记为 iPhone 问题,因此 NSLog 会占用资源,而 iPhone 几乎没有这种资源。如果您在iPhone 上对任何内容进行 NSLogging ,这会占用您应用程序的处理器时间。明智地使用它。
简单的事实是 NSLog 很慢。
但为什么?为了回答这个问题,让我们找出 NSLog 是做什么的,然后是它是如何做到的。
NSLog 到底是做什么的?
NSLog 做了两件事:
它将日志消息写入 Apple 系统日志 (asl) 工具。这允许日志消息显示在 Console.app 中。它还检查应用程序的 stderr 流是否将发送到终端(例如当应用程序通过 Xcode 运行时)。如果是这样,它将日志消息写入 stderr(以便它显示在 Xcode 控制台中)。
写到 STDERR 听起来并不难。这可以通过 fprintf 和 stderr 文件描述符参考来完成。但是 asl 呢?
我发现的关于 ASL 的最佳文档是 Peter Hosey 的 10 部分博客文章:链接
无需过多介绍,重点(因为它涉及性能)是这样的:
要将日志消息发送到 ASL 工具,您基本上打开一个到 ASL 守护程序的客户端连接并发送消息。但是 - 每个线程必须使用单独的客户端连接。因此,为了线程安全,每次调用 NSLog 时,它都会打开一个新的 asl 客户端连接,发送消息,然后关闭连接。
如其他答案所述,您可以使用 #define 来更改在编译时是否使用 NSLog。
然而,更灵活的方法是使用像Cocoa Lumberjack这样的日志库,它允许您更改是否也在运行时记录某些内容。
在您的代码中,将 NSLog 替换为 DDLogVerbose 或 DDLogError 等,为宏定义等添加 #import 并设置记录器,通常在 applicationDidFinishLaunching 方法中。
为了达到与 NSLog 相同的效果,配置代码为
[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
从安全的角度来看,这取决于记录的内容。如果NSLog
(或其他记录器)正在写入敏感信息,那么您应该在生产代码中删除记录器。
从审计的角度来看,审计员不想查看每次使用NSLog
以确保其不记录敏感信息。他/她只会告诉您删除记录器。
我与这两个小组一起工作。我们审核代码、编写编码指南等。我们的指南要求在生产代码中禁用日志记录。所以内部团队知道不要尝试;)
我们还将拒绝登录生产环境的外部应用程序,因为我们不想接受与意外泄露敏感信息相关的风险。我们不在乎开发商告诉我们什么。它根本不值得我们花时间去调查。
请记住,我们定义“敏感”,而不是开发人员;)
我还认为一个执行大量日志记录的应用程序是一个准备好崩溃的应用程序。执行/需要如此多的日志记录是有原因的,而且它通常不稳定。它就在那里,带有重新启动挂起服务的“看门狗”线程。
如果您从未通过安全架构 (SecArch) 审查,那么这些就是我们所关注的。
您不应该在发布代码中对 printf 或 NSLog 过于冗长。如果应用程序发生了不好的事情,请尝试仅执行 printf 或 NSLog,即无法恢复的错误。
请记住,NSLogs 会减慢 UI / 主线程的速度。除非绝对必要,否则最好将它们从发布版本中删除。
我强烈建议使用 TestFlight 进行日志记录(免费)。他们的方法将覆盖 NSLog(使用宏)并允许您为所有现有的 NSLog 调用打开/关闭到他们的服务器、Apple 系统日志和 STDERR 日志的日志记录。这样做的好处是,您仍然可以查看部署到测试人员的应用程序和部署在 App Store 中的应用程序的日志消息,而不会出现在用户的系统日志中。两全其美的。