122

When releasing an app for iPhone, if I disable NSLog(); will it perform better?

Nicolas Miari
  • 16,006
  • 8
  • 81
  • 189
RAGOpoR
  • 8,118
  • 19
  • 64
  • 97
  • 1
    In my current project I use [UALogger](https://github.com/UrbanApps/UALogger). It **doesn't log in production**, if you not ask it explicitly. And has other benefits over plain NSLog such as severity levels (with *DEBUG*, *INFO* and etc) out of box. Good job! – Anton Gaenko Feb 11 '14 at 10:54
  • 1
    To answer your question on "will it perform better?" Yes it does, but the yield that you get is dependent on how many `NSLog()` you have across your app. `NSLog()` takes time to execute and adds additional overhead to the runtime of your app. Anyways, if it helps in performance with a simple DEBUG preprocessor macro, then we should disable it. – Scott Nov 22 '14 at 11:18
  • I would also suggest if you have lots of NSLog/print statements in your code, it might suggest you should spend some time learning more about the debugger. I regularly set breakpoints that print information I'm interested in, and automatically continue. Yes, it can slow the running down a bit, but in most cases it's not overwhelming. Also, conditional breaks so you can investigate when something unexpected happened. – bshirley Jul 15 '19 at 16:50

12 Answers12

127

One way to do it is to go into your Build settings and under the Debug configuration add a value to "Preprocessor Macros" value like:

DEBUG_MODE=1

Make sure you only do this for the Debug configuration and not for Beta or Release versions. Then in a common header file you can do something like:

#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

Now instead of NSLog use DLog everywhere. When testing and debugging, you'll get debug messages. When you're ready to release a beta or final release, all those DLog lines automatically become empty and nothing gets emitted. This way there's no manual setting of variables or commenting of NSLogs required. Picking your build target takes care of it.

Ramin
  • 13,343
  • 3
  • 33
  • 35
  • In Xcode 4.5 it gives a warning that says: "Implicit declaration of function 'DLog' is invalid in C99" so this thing doesn't work. – Sergey Grischyov Jan 18 '13 at 16:34
  • 2
    @SergiusGee: You get the implicit declaration warning if the declaration for the function cannot be found, in which case it thinks you're trying to declare it. Make sure your class has access to the header file where this is declared. – sudo rm -rf Jan 30 '13 at 09:45
  • 1
    Id not take logging out as it robs you of the ability to receive better error reports from users. use async logging and logLevels to limit the performance hit to almost zero! (see cocoa lumberjack or java's log4j – Daij-Djan Jan 30 '13 at 16:57
  • 2
    I would go with #if rather than #ifdef, as DEBUG_MODE 0 will still go through the true path – Grady Player May 31 '13 at 18:31
  • i done the same but still not working. It is giving warning MR_MODE macro redefined MR_MODE(DEBUG_MODE) is in build setttings – Abhishek Thapliyal Jun 21 '16 at 16:58
  • 1
    this does not answer the question – Martin Mlostek Mar 29 '17 at 23:39
  • After using DLog, i getting "ARC Retain Cycle" warning when i logged from "blocks". Message is: "Capturing 'self' strongly in this block is likely to lead to a retain cycle". How to fix it? – Ganpat Mar 10 '18 at 10:36
119

Update for Xcode 5 & iOS 7

note : for a Xcode 7 / Swift 2.1 solution to remove print() statements in a release build, find my answer here.

Yes, you should remove any NSLog statement in your release code, as it just slows down your code, and isn't of any use in a release version. Fortunately, in Xcode 5 (iOS 7), it is amazingly simple to remove all your NSLog statements 'automatically' in release builds. So why not do it.

First the 3 steps to take, then some explanation

1) in your Xcode project, locate the 'yourProjectName-prefix.pch' file (normally you'll find this under the group 'supporting files', where your main.m file is located

2) add these 3 lines at the end of the '.pch' file :

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

3) test the difference between your 'debug' and 'release' version. One way to do this is through 'edit scheme' -> 'run app name' -> under the tab 'info' select using the drop-down box between debug & release. In the release version you won't see any NSLog output in the debug console !

How does this all work?

first of all, one must know that a preprocessor is relatively 'dumb', and just acts as a 'text replacer' before the compiler is called. It replaces anything you '#define' by what follows the #define statement.

#define NSLog(...);

The (...) stands for 'anything' between the brackets (). Mind also the ; at the end. This is not strictly necessary as the compiler will optimize this away, but I like to put it there, as it is more 'correct'. After our #define there is 'nothing', so the preprocessor will replace it with 'nothing', and so it will just throw away the complete line, starting at NSLog... until and including the ;.

define statements can be made conditional using #ifdef (if defined) or #ifndef (if not defined)

here we write #ifndef DEBUG, which means 'if the symbol DEBUG is not defined'. The #ifdef or #ifndef need to be 'closed' with #endif

Xcode 5 defines by default the 'DEBUG' symbol for us when de build mode is 'DEBUG'. In 'release' this is not defined. you can verify this under your project settings, tab 'Build settings' -> scroll down to the section 'Apple LLVM 5.0 - Preprocessing' -> preprocessor macros. You'll see that the symbol 'DEBUG' is not defined for release builds !

finally, the .pch file is created by Xcode automatically, and automatically included in every source file during the compilation time. So it is as if you would have put the whole #define thing into each of your source files.

Community
  • 1
  • 1
Ronny Webers
  • 5,244
  • 4
  • 28
  • 24
  • 1
    Thanks @Whasssaaahhh, it works great. Be careful to avoid putting code in log statements! The preprocessor will *remove* the whole `NSLog` statements disregarding what is inside. – Eric Platon May 29 '14 at 02:00
  • 1
    If it's an older project that hasn't got the debug flag in preprocesssor macros it's importen to add the "debug=1" for the project and not the target – Priebe Sep 01 '15 at 08:58
  • 1
    Also, don't use `NSLog` as a do nothing statement, e.g. `if(AllCool) NSLog(@"Cool!Do Nothing!"); else...` instead pop the `NSLog` in some curly brackets `if(AllCool) {NSLog(@"Cool!Do Nothing!");} else...` – arcady bob Jan 13 '16 at 04:47
  • This results in wrong code, when there are no curly braces around the NSLog. – Tom Jun 28 '21 at 20:37
  • remove the ; use this: #define NSLog(...) – Tom Jun 28 '21 at 22:29
34

Almost all above answers sugest a solution but not explain the problem. I did a search in google, and found the reason. Here is my answer:

Yes, if you comment out NSLog in your release version, the performance will become better. Because NSLog is pretty slow. Why? NSLog will do two things 1) write log messages to Apple System Logging(ASL), 2) if the app runs in xcode it write to stderr too.

The main problem lays in the first one. In order to achieve thread safe, every time NSLog is called, it opens an connection to ASL facility, sends message, and closes the connection. The connection operation is very expensive. Another reason is that NSLog spends some time to get the timestamp to log.

Reference from here.

zhongwuzw
  • 156
  • 1
  • 1
  • 8
Andrew
  • 1,088
  • 10
  • 21
23

My personal favourite is to use a variadic macro.

#ifdef NDEBUG
    #define NSLog(...) /* suppress NSLog when in release mode */
#endif
Eytan
  • 1,825
  • 17
  • 24
21

In addition to all the people who wisely commented that not calling NSLog() at all in production runs slightly faster, I'll add that:

All those NSLog() output strings are visible to anyone who downloads your app from the store and runs it with the device plugged into a mac running Xcode (through the Organizer window).

Depending on what information you log (and especially if your app contacts a server, does authentication, etc.), this can be a serious security issue.

Nicolas Miari
  • 16,006
  • 8
  • 81
  • 189
  • thanks for the info - is this in the doc somewhere, or just discovered that yourself? Is it still true for print in Swift? – Ronny Webers Dec 13 '15 at 15:05
  • I don't remember reading any documentation. I just installed my archived build (same binary I submitted to the store) on my device and plugged it to Xcode. I have no idea if it's the same for Swift's `print()`, but most likely it is. – Nicolas Miari Dec 14 '15 at 01:46
  • @NicolasMiari What do you mean by plugged into Xcode? How we can plug our binary into Xcode, actually I want to try the same. So please suggest. Thanks. – iDevAmit Jan 06 '16 at 08:40
  • @iDeveloper I mean download your app from the AppStore to a device (e.g., an iPhone), plug that device to Xcode via USB, launch your app and watch the logs in Xcode's "Devices" window. – Nicolas Miari Jan 06 '16 at 08:43
  • @NicolasMiari ok, I got it. Thanks. – iDevAmit Jan 06 '16 at 08:47
  • 3
    @Whasssaaahhh print doesnot output in the device console..i just tested that – LC 웃 Mar 11 '16 at 11:59
14

Project Default Setting

Inside current default setting of project in Xcode, the NS_BLOCK_ASSERTIONS macro will be set to 1 in release version, and DEBUG=1 in Debug version.

So, I prefer the following method.

// 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
Kevin Chen
  • 994
  • 8
  • 24
AechoLiu
  • 17,522
  • 9
  • 100
  • 118
5

All good answers, however here's another little trick you can consider using, mainly in the development/testing phases of your app.

It could also be useful for app release code also, if you only want to turn of YOUR debug code, and not messages that might indicate issues outside of your code's direct control.

The Trick:

You can turn off NSLog per .m file by simply including the follow line at the top of the .m file:

#define NSLog(...)

(NOTE: do NOT put this the .h file, only the .m file!)

This just makes the compiler evaluates NSLog() by expanding your preprocessor macro instead. The macro does nothing but strip out the arguments.

if you want to turn it back on again you can always use

#undef NSLog

You could for example just prevent out calls to NSLog around a particular group of methods by doing something like

#define NSLog(...)
-(void) myProblematicMethodThatSometimesNeedsDebugging {
    ...
}
#undef NSLog
unsynchronized
  • 4,828
  • 2
  • 31
  • 43
5

Yes, you should disable it. Especially if you're trying to maximize the speed of your code. NSLogging things left and right pollutes the system log that other developers might be trying to dig through and it can have a big impact on speed-critical code (inside loops, etc..) I accidentally left some log messages in a recursive function once and got to release an update with a "30% speed increase!" a few weeks later... ;-)

Ben Gotow
  • 14,805
  • 3
  • 42
  • 47
3

NSLog is slow and should not be used for release builds. A simple macro like the one below will disable it along with any asserts you might have which should also be disabled. In the less common case where you do want NSLog in a release build, just call it directly. Don't forget to add "-DNDEBUG" to your "other c flags" build settings.

#ifdef NDEBUG
#define MYLog(f, ...) 
#else
#define MYLog(f, ...) NSLog(f, ## __VA_ARGS__)
#endif
papahabla
  • 1,448
  • 18
  • 18
  • Xcode already has a DEBUG define you can use. It will be automatically set to 0 in release mode. – Tom Jun 28 '21 at 19:08
2

in pch file write down this before #endif

#define NSLog() //
mmmmmm
  • 32,227
  • 27
  • 88
  • 117
0
var showDebugLogs = false;

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

This will accept the additional arguments too.. Just the showDebugLogs parameter value to true or false, as per your need

Zahur
  • 181
  • 3
  • 10
  • This is nice, but still has the problem of all the call overhead and of any overhead (and potential side effects) of computing any arguments passed to the `Dlog` function. – Todd Lehman Mar 08 '16 at 08:15
0

what about this?

#ifndef DEBUG_MODE
        fclose(stderr);     // the simplest way to disable output from NSLog
#endif    
roberto.buratti
  • 2,487
  • 1
  • 16
  • 10
  • 1
    this disables the output, but doesn't save any processing time, i.e. NSLog is still called and its arguments parsed – dwery Nov 18 '14 at 08:56
  • But this avoids errors, when there are no braces around the NSLog. – Tom Jun 28 '21 at 21:15