views:

4204

answers:

9

I was told this a few times in this very site, but I wanted to make sure this is really the case.

I was expecting to be able to sprinkle NSLog function calls throughout my code, and that Xcode/gcc would automatically strip those calls out when building my Release/Distribution builds.

Should I avoid using this? If so, what alternatives are most common between experienced Objective-C programmers?

+9  A: 

NSLog calls can be left in production code, but should only be there for truly exceptional cases, or information that it is desired that will be logged to the system log.

Applications which litter the system log are annoying, and come across as unprofessional.

Matthew Schinckel
+7  A: 

EDIT: The method posted by Marc Charbonneau, and brought to my attention by sho, is far better than this one.

I have deleted the portion of my answer which suggested using an empty function to disable logging when debug mode is disabled. The portion that deals with setting an automatic preprocessor macro is still relevant, so it remains. I have also edited the name of the preprocessor macro so that it fits better with Marc Charbonneau's answer.


To achieve the automatic (and expected) behaviour in Xcode:

In the project settings, go to the "Build" tab, and select the "Debug" configuration. Find the "Preprocessor Macros" section, and add a macro named DEBUG_MODE.

...

EDIT: See Marc Charbonneau's answer for the proper way to enable and disable logging with the DEBUG_MODE macro.

e.James
+5  A: 

I agree with Matthew. There's nothing wrong with NSLog in production code. In fact, it can be useful to the user. That said, if the only reason you're using NSLog is to help debug, then, yes, that should be removed before you release.

Furthermore, since you've tagged this as an iPhone question, NSLog takes resources, which is something the iPhone has precious little of. If you're NSLogging anything on the iPhone, that takes away processor time from your app. Use it wisely.

August
+52  A: 

Preprocessor macros are indeed great for debugging. There's nothing wrong with NSLog(), but it's simple to define your own logging function with better functionality. Here's the one I use, it includes the file name and line number to make it easier to track down log statements.

#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

I found it easier to put this entire statement in the prefix header rather than it's own file. You could, if you wanted, build a more complicated logging system by having DebugLog interact with normal Objective-C objects. For instance, you could have a logging class that writes to its own log file (or database), and includes a 'priority' argument you could set at runtime, so debug messages are not shown in your release version, but error messages are (if you did this you could make DebugLog(), WarningLog(), and so on).

Oh, and keep in mind #define DEBUG_MODE can be re-used in different places in your application. For example, in my application I use it to disable license key checks and only allow the application to run if it's before a certain date. This lets me distribute a time limited, fully functional beta copy with minimal effort on my part.

Marc Charbonneau
e.James
An excellent answer, though I recommend using a personal prefix on your "DEBUG_MODE" define, such as calling it "JPM_DEBUG" or the like. Far too often I've encountered third party code that also uses DEBUG or DEBUG_MODE or the like, and sometimes that code will not work correctly in DEBUG mode. If you want to turn on third-party library debugging, you should do that intentionally. (Of course, it's library writers who should be prefixing their symbols, but many C and C++ frameworks do not, especially for this define).
Rob Napier
is there an Xcode predefined macro that can be used to turn this on only when the configuration is set to debug? I'd rather not manually set this preprocessor macro myself in every project. can we do something like following pseudocode #if XCODE_CONFIGURATION==DEBUG ?
frankodwyer
#include <TargetConditionals.h>
slf
http://www.visualco.de/NDEBUG.html
slf
http://stackoverflow.com/questions/2017108/how-to-setup-xcodes-debug-release-target-settings
slf
This approach leads to spurious "unused variables" warnings from the compiler in release mode when the logging statements use intermediary variables for the sole purpose of computing the values to log. What would be the smartest way to avoid that if you hate compiler warnings as much as I do?
Jean-Denis Muys
+4  A: 

Hm. I can't seem to vote anything up because I just registered. Could someone please vote up the preprocessor macro solution by Marc Charbonneau over the empty function solution by eJames?

Preprocessor macros are the right thing for stubbing out debug-only code, because they make sure that the code inside them doesn't get run.

Example -- showing the results of an HTTP call:

DebugLog(@"%@",[[[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding] autorelease]);

With the preprocessor macro, none of the code inside the parentheses will be executed unless the program is compiled in debug mode. With the stubbed out function, the code inside the parentheses (allocating the string and converting the received data) will be called even when you don't need it.

sho
Maybe you should make a comment about this on eJames's answer...
Matthew Schinckel
@sho: This is a very good point. I wish I had seen your comment sooner. I was using the empty function because I didn't think you could use #define MyLog(format, ...) with macros. Marc Charbonneau's answer is the correct one.
e.James
A: 

Is there a way to know the name of the method where DebugLog() is called?

+1  A: 

I have the answer to my own question:

Use __FUNCTION__ with "%s" to know the name of the function where DebugLog was called.

Example:

#define DEBUG_MODE

#ifdef DEBUG_MODE
    #define DebugLog( s, ... ) NSLog( @"<%s : (%d)> %@",__FUNCTION__, __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
    #define DebugLog( s, ... ) 
#endif
Use __PRETTY_FUNCTION__ instead. It's the same for C, but prints pretty Objective-C method names. :-)
Quinn Taylor
I use __func__ which seems to do the same thing.
Wevah
That's `__func__`, of course. (Hopefully this won't bold again.)
Wevah
+4  A: 

I can't comment on Marc Charbonneau's answer, so I'll post this as an answer.

Further to adding the macro to your pre-compiled header, you can use the Target build configurations to control the defining (or lack of defining) the DEBUG_MODE.

If you select "Debug" active configuration, DEBUG_MODE will be defined, and the macro expands to the full NSLog definition.

Selecting the "Release" active configuration will not define DEBUG_MODE and your NSLogging is omitted from the release build.

Steps:

  • Target > Get Info
  • Build tab
  • Search for "PreProcessor Macros" (or GCC_PREPROCESSOR_DEFINITIONS)
  • Select Configuration: Debug
  • Edit Definition at this Level
  • Add DEBUG_MODE=1
  • Select Configuration: Release
  • confirm DEBUG_MODE is not set in GCC_PREPROCESSOR_DEFINITIONS

if you omit the '=' character in the definition, you will get an error from the preprocessor

Also, paste this comment (shown below) above the macro definition to remind you where the DEBUG_MACRO define comes from ;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1
ohhorob
A: 

This question is broad. Try again.

Lane