Thursday, May 19, 2011

Stuff I Can't Live Without Part I: conditional NSLogging

Note: This blog is deprecated. @synthesize zach has moved to a new home, at zpasternack.org. This blog entry can be found on the new blog here. Please update your links.

I spend a great deal of time watching my code. By that I mean either stepping through it in the debugger, or reading logs generated by it to see what it’s done. You might think you know exactly what your software is doing, but the truth is, if you don’t watch it run, you don’t know.

During the course of development, I usually end up adding a fair amount of logging code, and of course I don’t want that in my production builds. So a while back I went a-Googling for some conditional NSLog calls, and found these great macros, courtesy of Nick Dalton’s iPhoneIncubator. I use them (and repeat them below) nearly verbatim. This is the first thing I add to any project I’m working on. I’m naked without my DebugLog (and not in the good way).

// DebugLog is almost a drop-in replacement for NSLog  
// DebugLog();  
// DebugLog(@“here”);  
// DebugLog(@“value: %d”, x);  
// Unfortunately this doesn’t work: DebugLog(aStringVariable); you have to do this instead: DebugLog(@“%@“, aStringVariable); 
// Shamelessly stolen (and edited) from http://iphoneincubator.com/blog/debugging/the-evolution-of-a-replacement-for-nslog

#ifdef DEBUG  
 #define DebugLog(fmt, ...) NSLog((@“%s:%d “ fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
 #define DebugLogFunc() NSLog( @“%s:%d”, __PRETTY_FUNCTION__, __LINE__ );
#else  
 #define DebugLog(...) 
 #define DebugLogFunc() 
#endif  

// AlwaysLog always displays output regardless of the DEBUG setting  
#define AlwaysLog(fmt, ...) NSLog((@“%s:%d “ fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);  

Of course this assumes you have DEBUG #defined in your debug builds somewhere (in Xcode 4 do this in Preprocessor Macros in Build Settings).

The only changes I made over Nick’s is renaming them (I prefer clarity over brevity), minor formatting, and adding the ...Func macros for the common (for me) case of simply outputting the function name with no further description. This is useful for making sure the functions you expect are getting called, really are.