【问题标题】:Do I need to disable NSLog before release Application?我需要在发布应用程序之前禁用 NSLog 吗?
【发布时间】:2011-01-02 18:51:50
【问题描述】:

在为 iPhone 发布应用程序时,如果我禁用 NSLog();,它的性能会更好吗?

【问题讨论】:

  • 在我当前的项目中,我使用UALogger。如果您没有明确询问,它不会登录生产。并且与普通的 NSLog 相比还有其他好处,例如开箱即用的严重级别(带有 DEBUGINFO 等)。干得好!
  • 回答您关于“性能会更好吗?”的问题。是的,但您获得的收益取决于您在整个应用程序中拥有多少NSLog()NSLog() 需要时间来执行并为您的应用程序的运行时增加额外的开销。无论如何,如果它有助于使用简单的 DEBUG 预处理器宏来提高性能,那么我们应该禁用它。
  • 我还建议如果你的代码中有很多 NSLog/print 语句,它可能建议你应该花一些时间来了解更多关于调试器的信息。我会定期设置断点来打印我感兴趣的信息,然后自动继续。是的,它可以稍微减慢运行速度,但在大多数情况下,它并不是压倒性的。此外,还有条件中断,以便您在发生意外情况时进行调查。

标签: ios iphone nslog


【解决方案1】:

一种方法是进入您的构建设置并在调试配置下为“预处理器宏”值添加一个值,例如:

DEBUG_MODE=1

确保您只对调试配置执行此操作,而不是对 Beta 或 Release 版本执行此操作。然后在一个通用的头文件中,您可以执行以下操作:

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

现在到处都使用DLog 而不是NSLog。在测试和调试时,您会收到调试消息。当您准备发布测试版或最终版时,所有这些 DLog 行会自动变为空,并且不会发出任何内容。这样就不需要手动设置变量或注释NSLogs。选择你的构建目标就可以了。

【讨论】:

  • 在 Xcode 4.5 中它给出了一个警告,上面写着:“函数 'DLog' 的隐式声明在 C99 中是无效的”所以这个东西不起作用。
  • @SergiusGee:如果找不到函数的声明,您会收到隐式声明警告,在这种情况下,它会认为您正在尝试声明它。确保你的类可以访问声明它的头文件。
  • 我不要注销,因为它会剥夺您从用户那里接收更好的错误报告的能力。使用异步日志记录和 logLevels 将性能损失限制在几乎为零! (参见 cocoa lumberjack 或 java 的 log4j
  • 我会选择 #if 而不是 #ifdef,因为 DEBUG_MODE 0 仍将通过真正的路径
  • 这没有回答问题
【解决方案2】:

Xcode 5 和 iOS 7 更新

注意:对于在发布版本中删除 print() 语句的 Xcode 7 / Swift 2.1 解决方案,请找到我的答案 here。 em>

是的,您应该删除发布代码中的任何 NSLog 语句,因为它只会减慢您的代码速度,并且在发布版本中没有任何用处。幸运的是,在 Xcode 5 (iOS 7) 中,在发布版本中“自动”删除所有 NSLog 语句非常简单。那么为什么不这样做呢。

首先要采取的 3 个步骤,然后是一些解释

1) 在您的 Xcode 项目中,找到“yourProjectName-prefix.pch”文件(通常您会在您的 main.m 文件所在的“支持文件”组下找到它

2) 在“.pch”文件的末尾添加这 3 行:

#ifndef DEBUG
   #define NSLog(...);
#endif

3) 测试您的“调试”和“发布”版本之间的差异。一种方法是通过“编辑方案”->“运行应用程序名称”-> 在“信息”选项卡下使用调试和发布之间的下拉框进行选择。在发布版本中,您不会在调试控制台中看到任何 NSLog 输出!

这一切是如何运作的?

首先,必须知道预处理器相对“笨”,在调用编译器之前只是充当“文本替换器”。它用#define 语句后面的内容替换您“#define”的任何内容。

#define NSLog(...);

(...) 代表括号 () 之间的“任何东西”。最后还要注意;。这不是绝对必要的,因为编译器会优化它,但我喜欢把它放在那里,因为它更“正确”。在我们的#define 之后有'nothing',所以预处理器会将它替换为'nothing',所以它会丢弃整行,从NSLog... 开始直到并包括;

define 语句可以使用#ifdef(如果已定义)或#ifndef(如果未定义)来设置条件

这里我们写#ifndef DEBUG,意思是'如果符号DEBUG没有定义'。 #ifdef#ifndef 需要使用 #endif“关闭”

当 de build mode 为“DEBUG”时,Xcode 5 默认为我们定义了“DEBUG”符号。在“发布”中,这是没有定义的。您可以在项目设置下验证这一点,选项卡“构建设置”-> 向下滚动到“Apple LLVM 5.0 - 预处理”-> 预处理器宏部分。您会看到符号“DEBUG”没有为发布版本定义!

最后,.pch 文件由 Xcode 自动创建,并在编译期间自动包含在每个源文件中。因此,就好像您将整个 #define 内容放入每个源文件中一样。

【讨论】:

  • 谢谢@Whasssaaahhh,效果很好。小心避免将代码放在日志语句中!预处理器将删除整个NSLog 语句,不管里面是什么。
  • 如果它是一个较旧的项目,在预处理器或宏中没有​​调试标志,则为项目而不是目标添加“debug=1”是很重要的
  • 另外,不要使用NSLog 作为无所事事的声明,例如if(AllCool) NSLog(@"Cool!Do Nothing!"); else... 而是将 NSLog 插入一些大括号 if(AllCool) {NSLog(@"Cool!Do Nothing!");} else...
  • 当 NSLog 周围没有大括号时,这会导致错误的代码。
  • 删除 ;使用这个:#define NSLog(...)
【解决方案3】:

几乎所有上述答案都提出了解决方案,但没有解释问题。我在google上搜索了一下,找到了原因。这是我的答案:

是的,如果您在发布版本中注释掉 NSLog,性能会变得更好。因为 NSLog 很慢。 为什么? NSLog 将做两件事 1) 将日志消息写入 Apple System Logging(ASL),2) 如果应用程序在 xcode 中运行,它也会写入 stderr。

主要问题在于第一个问题。为了实现线程安全,每次调用 NSLog,它都会打开与 ASL 设施的连接,发送消息,然后关闭连接。连接操作非常昂贵。另一个原因是 NSLog 花费一些时间来获取要记录的时间戳。

来自here的参考。

【讨论】:

    【解决方案4】:

    我个人最喜欢的是使用可变参数宏。

    #ifdef NDEBUG
        #define NSLog(...) /* suppress NSLog when in release mode */
    #endif
    

    【讨论】:

    • 你把这个放在哪里?
    • 当 NSLog 周围没有大括号时,它必须是 #ifdef DEBUG 并且你得到错误的代码
    【解决方案5】:

    除了所有明智地评论说在生产中根本不调用 NSLog() 运行速度稍快的人之外,我还要补充一点:

    所有这些NSLog() 输出字符串对于从商店下载您的应用程序并使用插入运行 Xcode 的 mac 的设备运行它的任何人都是可见的(通过管理器窗口)。

    根据您记录的信息(尤其是当您的应用与服务器联系、进行身份验证等时),这可能是一个严重的安全问题

    【讨论】:

    • 感谢您提供信息 - 这是在文档中的某个地方,还是您自己发现的?在 Swift 中打印仍然如此吗?
    • 我不记得阅读过任何文档。我刚刚在我的设备上安装了我的存档构建(我提交给商店的相同二进制文件)并将其插入 Xcode。我不知道 Swift 的 print() 是否相同,但很可能是。
    • @NicolasMiari 插入 Xcode 是什么意思?我们如何将二进制文件插入 Xcode,实际上我也想尝试一下。所以请建议。谢谢。
    • @iDeveloper 我的意思是从 AppStore 将您的应用程序下载到设备(例如 iPhone),将该设备通过 USB 插入 Xcode,启动您的应用程序并在 Xcode 的“设备”窗口中查看日志。
    • @Whasssaaahhh 打印不会在设备控制台中输出..我刚刚测试过
    【解决方案6】:

    在 Xcode 项目的当前默认设置中,NS_BLOCK_ASSERTIONS 宏将在发布版本中设置为 1,在调试版本中设置为 DEBUG=1

    所以,我更喜欢下面的方法。

    // NS_BLOCK_ASSERTIONS is defined by default, as shown in the screenshot above.
    // Or, you can define yourself Flags in the `Other C Flags` -> `Release`.
    #ifndef NS_BLOCK_ASSERTIONS
        #define _DEBUG
    #endif
    
    #ifdef _DEBUG
    // for debug mode 
    #define DLog(fmt,...) NSLog(@"%s " fmt, __FUNCTION, ##__VA_ARGS__) 
    ... /// something extra
    #else
    // for release mode
    #define DLog(fmt,...) /* throw it away */
    ... /// something extra
    #endif
    

    【讨论】:

    • 有一个错字,应该是#ifdef DEBUG
    • @Tom #define _DEBUG。如果未定义 NS_BLOCK_ASSERTIONS,则该行将定义 _DEBUG
    【解决方案7】:

    是的,您应该禁用它。特别是如果您试图最大限度地提高代码的速度。 NSLogging 左右的东西会污染其他开发人员可能试图挖掘的系统日志,并且它可能对速度关键代码(内部循环等)产生很大影响。我不小心在递归函数中留下了一些日志消息一次并且必须发布“速度提升 30%”的更新!几周后... ;-)

    【讨论】:

      【解决方案8】:

      所有好的答案,但是您可以考虑使用另一个小技巧,主要用于应用程序的开发/测试阶段。

      如果您只想关闭您的调试代码,而不是可能表明您的代码无法直接控制的问题的消息,它也可能对应用发布代码有用。

      诀窍:

      您可以关闭每个 .m 文件的 NSLog,只需在 .m 文件顶部添加以下行

      #define NSLog(...)
      

      注意:不要把这个放在 .h 文件中,只放在 .m 文件中!

      这只是让编译器通过扩展你的预处理器宏来评估NSLog()。宏除了去掉参数什么都不做。

      如果你想再次打开它,你可以随时使用

      #undef NSLog
      

      例如,您可以通过执行类似的操作来阻止围绕特定方法组对 NSLog 的调用

      #define NSLog(...)
      -(void) myProblematicMethodThatSometimesNeedsDebugging {
          ...
      }
      #undef NSLog
      

      【讨论】:

        【解决方案9】:

        NSLog 很慢,不应该用于发布版本。一个像下面这样的简单宏将禁用它以及您可能拥有的任何断言,这些断言也应该被禁用。在不太常见的情况下,您确实希望在发布版本中使用 NSLog,只需直接调用它即可。不要忘记将“-DNDEBUG”添加到您的“其他 c 标志”构建设置中。

        #ifdef NDEBUG
        #define MYLog(f, ...) 
        #else
        #define MYLog(f, ...) NSLog(f, ## __VA_ARGS__)
        #endif
        

        【讨论】:

        • Xcode 已经有一个可以使用的 DEBUG 定义。在发布模式下会自动设置为0。
        【解决方案10】:

        在 pch 文件中 #endif 之前写下这个

        #define NSLog() //
        

        【讨论】:

          【解决方案11】:

          这个呢?

          #ifndef DEBUG_MODE
                  fclose(stderr);     // the simplest way to disable output from NSLog
          #endif    
          

          【讨论】:

          • 这会禁用输出,但不会节省任何处理时间,即仍然调用 NSLog 并解析其参数
          • 但是当 NSLog 周围没有大括号时,这样可以避免错误。
          【解决方案12】:
          var showDebugLogs = false;
          
              func DLog(format: String, args: CVarArgType...) {
                  if showDebugLogs{
                  println(String(format: format, arguments: args))
                  }
              }
          

          这也将接受附加参数.. 只需将 showDebugLogs 参数值设置为 true 或 false,根据您的需要

          【讨论】:

          • 这很好,但仍然存在所有调用开销以及计算传递给Dlog 函数的任何参数的任何开销(和潜在的副作用)的问题。
          猜你喜欢
          • 1970-01-01
          • 2021-04-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-13
          • 1970-01-01
          • 2011-11-24
          相关资源
          最近更新 更多