我在这个网站上被告知这个几次,但我想确定这是否真的是这样。
我本来期望可以在我的代码中撒布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的答案发表评论,所以我会将这个作为答案发布。
在将宏添加到您的预编译头文件后,您可以使用目标构建配置来控制定义(或不定义)DEBUG_MODE
。
如果你选择激活“调试”配置,DEBUG_MODE
将被定义,并且该宏将扩展为完整的NSLog
定义。
选择“发行”活动配置不会定义DEBUG_MODE,您的NSLog记录将从发布构建中省略。
步骤:
GCC_PREPROCESSOR_DEFINITIONS
)DEBUG_MODE=1
DEBUG_MODE
is not set in 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
的宏。
I'm sorry, but what do you want me to translate? Please provide the text or phrase.
编辑:查看Marc Charbonneau的答案,了解使用DEBUG_MODE
宏启用和禁用日志记录的正确方法。
我同意Matthew的观点。在生产代码中使用NSLog并没有什么问题。实际上,它对用户有用。话虽如此,如果您使用NSLog的唯一原因是帮助调试,那么在发布之前应该将其删除。
此外,由于你标记了这是一个iPhone问题,NSLog需要资源,而iPhone缺乏宝贵的资源。如果你在iPhone上记录任何东西,那么它会从你的应用程序中抽取处理器时间。明智地使用它。
简单的事实是,NSLog非常缓慢。
但为什么?为了回答这个问题,让我们找出 NSLog 的作用,然后了解它的工作原理。
NSLog的确切作用是什么?
NSLog可以做两件事情:
It writes log messages to the Apple System Logging (asl) facility. This allows log messages to show up in Console.app. It also checks to see if the application s stderr stream is going to a terminal (such as when the application is being run via Xcode). If so it writes the log message to stderr (so that it shows up in the Xcode console).
写到 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。
请记住,NSLog可能会减慢UI /主线程的速度。除非绝对必要,否则最好在发布版本中将其删除。
我强烈建议使用TestFlight进行日志记录(免费)。他们的方法将覆盖NSLog(使用宏)并允许您打开/关闭所有现有NSLog调用的日志记录到他们的服务器、Apple系统日志和STDERR日志。好处在于,您仍然可以查看已部署到测试人员的应用程序和已部署到App Store的应用程序的日志消息,而不会出现在用户的系统日志中。两全其美。