发布应用程序之前是否需要禁用NSLog?


117

发行iPhone版应用程序时,如果禁用NSLog();它,性能会更好吗?


1
在当前项目中,我使用UALogger。如果您没有明确询问,它不会登录生产。与普通的NSLog相比,它还有其他好处,例如开箱即用的严重性级别(带有DEBUGINFO等)。做得好!
2014年

1
回答有关“性能会更好?”的问题 是的,的确如此,但是您获得的收益取决于NSLog()整个应用程序中有多少。NSLog()需要花费时间才能执行,并增加了应用程序运行时的额外开销。无论如何,如果使用简单的DEBUG预处理程序宏有助于提高性能,则应禁用它。
斯科特

我还建议如果代码中包含许多NSLog / print语句,则可能建议您花一些时间来学习有关调试器的更多信息。我定期设置断点以打印我感兴趣的信息,然后自动继续。是的,它可以稍微减慢运行速度,但是在大多数情况下,它并不会让人感到不知所措。另外,有条件的中断使您可以调查意外情况何时发生。
bshirley19年

Answers:


127

一种方法是进入Build设置,然后在Debug配置下将一个值添加到“ Preprocessor Macros”值中,例如:

DEBUG_MODE=1

确保仅对调试配置执行此操作,而不对Beta版或发行版执行此操作。然后,在公共头文件中,您可以执行以下操作:

#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

现在,而不是到处NSLog 使用DLog。在测试和调试时,您会收到调试消息。当您准备发布Beta或最终版本时,所有这些DLog行都会自动变空并且不发出任何内容。这样就无需手动设置变量或注释NSLogs必需项。选择构建目标即可解决这一问题。


在Xcode 4.5中,它给出警告,指出:“函数'DLog'的隐式声明在C99中无效”,因此该操作不起作用。
Sergey Grischyov

2
@SergiusGee:如果找不到函数的声明,则会收到隐式声明警告,在这种情况下,它认为您正在尝试声明它。确保您的班级可以访问声明了该文件的头文件。
sudo rm -rf

1
请不要注销,因为它使您无法从用户那里收到更好的错误报告。使用异步日志记录和logLevels将性能命中率限制为几乎为零!(请参阅可可伐木工人或Java的log4j
Daij-Djan

2
我将使用#if而不是#ifdef,因为DEBUG_MODE 0仍将走在真实的道路上
Grady Player

1
这不能回答问题
Martin Mlostek

117

Xcode 5和iOS 7更新

注意:对于要在发行版中删除print()语句的Xcode 7 / Swift 2.1解决方案,请在此处找到我的答案。

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

首先执行3个步骤,然后进行一些解释

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

2)将这3行添加到'.pch'文件的末尾:

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

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

这一切如何运作?

首先,必须知道预处理器相对“笨拙”,并且在调用编译器之前仅充当“文本替换器”。它用#define语句后的内容替换您“ #define”的所有内容。

#define NSLog(...);

(...)代表的括号“什么”()。;最后也要注意。由于编译器会对其进行优化,因此这并非绝对必要,但我喜欢将其放在此处,因为它更“正确”。在我们之后#define没有“什么”,因此预处理器将其替换为“什么”,因此它将丢弃整个行,从NSLog...直到并包括;

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

在这里,我们写#ifndef DEBUG,意思是“如果未定义符号DEBUG”。在#ifdef#ifndef将需要“封闭”与#endif

当构建模式为“ DEBUG”时,Xcode 5默认为我们定义“ DEBUG”符号。在“发行版”中未定义。您可以在项目设置下的“构建设置”选项卡中进行验证->向下滚动至“ Apple LLVM 5.0-预处理”部分->预处理器宏。您会看到未为发行版定义符号'DEBUG'!

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


1
谢谢@Whasssaaahhh,效果很好。注意避免将代码放在日志语句中!预处理程序将删除整个NSLog语句,无论内部内容如何。
埃里克·普拉顿

1
如果它是一个较旧的项目,但在preprocesssor宏中没有调试标志,则要为该项目而不是目标添加“ debug = 1”
Priebe 2015年

1
另外,请勿将其NSLog用作“不做任何事情”的声明,例如,将“ if(AllCool) NSLog(@"Cool!Do Nothing!"); else...pop” NSLog放到大括号中if(AllCool) {NSLog(@"Cool!Do Nothing!");} else...
arcady bob

33

以上几乎所有答案都提出了解决方案,但没有解释问题。我在Google中进行了搜索,并找到了原因。这是我的答案:

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

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

参考从这里


23

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

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

1
你把它放在哪里?
user6631314 '18

20

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

所有NSLog()从商店下载您的应用并通过将设备插入运行Xcode的Mac(通过Organizer窗口)运行该应用的人都可以看到所有这些输出字符串

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


感谢您提供的信息-这在文档中是否存在,或者只是发现您自己?在Swift中打印仍然适用吗?
罗尼·韦伯斯

我不记得阅读任何文档。我只是在设备上安装了存档的版本(提交给商店的二进制文件),然后将其插入Xcode。我不知道Swift的代码是否相同print(),但很可能是这样。
Nicolas Miari 2015年

@NicolasMiari插入Xcode是什么意思?我们如何将二进制文件插入Xcode,实际上我想尝试相同的方法。所以请提出建议。谢谢。
iDevAmit

@iDeveloper我的意思是将您的应用程序从AppStore下载到设备(例如iPhone),通过USB将设备插入Xcode,启动您的应用程序,并在Xcode的“设备”窗口中查看日志。
Nicolas Miari '16

3
@Whasssaaahhh打印不会在设备控制台中输出。.我刚刚测试过
Anish Parajuli웃

13

项目默认设置

在Xcode中当前项目的默认设置内,NS_BLOCK_ASSERTIONS宏将在发行版和DEBUG=1调试版中设置为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

5

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


5

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

如果您只想打开自己的调试代码,而不是可能指示代码直接控制范围之外的消息的消息,则它对于应用程序发布代码也很有用。

绝招:

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

#define NSLog(...)

注意:请勿将.h文件放到此文件中,而仅将.m文件放进去!

这只会使编译器NSLog()通过扩展预处理器宏来进行评估。该宏不执行任何操作,但去除了参数。

如果您想重新打开它,可以随时使用

#undef NSLog

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

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

3

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

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


0

那这个呢?

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

1
这将禁用输出,但不会节省任何处理时间,即仍调用NSLog并解析其参数
dwery

0
var showDebugLogs = false;

    func DLog(format: String, args: CVarArgType...) {
        if showDebugLogs{
        println(String(format: format, arguments: args))
        }
    }

这也将接受其他参数。.根据您的需要,将showDebugLogs参数值设置为true或false


这很好,但是仍然存在所有调用开销以及计算传递给Dlog函数的任何参数的开销(以及潜在的副作用)的问题。
托德·雷曼
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.