Apr 03

My previous post about conditional logging received quite a few responses, with several people pointing out that littering your code with #ifdef DEBUG statements is both ugly and error prone. Karl Kraft, for example, created a new DebugLog class as a drop-in replacement for NSLog.

Since NSLog has been around for quite a while, there have been many solutions to this problem.

CocoaDev suggests this macro:

#if MY_DEBUG_FLAG
#define DEBUG_OUTPUT( a )         NSLog( a )
#define DEBUG_OUTPUT1( a, b )     NSLog( a, b )
#else
#define DEBUG_OUTPUT( a )         // (a)
#define DEBUG_OUTPUT1( a, b )     // (a,b)
#endif

Enumerating all the variants with different number of parameters gets a bit tedious, so with Objective-C 2.0 C99 you can use varargs macros:

#define DEBUG_OUTPUT(fmt, ...) NSLog(fmt, ## __VA_ARGS__)

As described by Fraser Hess in Dropping NSLog in release builds you can put this in your <AppName>_Prefix.pch file:

#ifdef DEBUG
#    define DLog(...) NSLog(__VA_ARGS__)
#else
#    define DLog(...) /* */
#endif
#define ALog(...) NSLog(__VA_ARGS__)

Use ALog for cases where you always want log output regardless of the state of the debug flag. I like this preprocessor macro approach because you don’t have to carry around extra classes in all your projects. Just a few lines of code to paste into one file in your project.

But I think we can improve it further.

The built-in macro __PRETTY_FUNCTION__ is pretty nifty in that it outputs the name of the class and the method where it’s running. Mark Damon Huges uses this in his Cocoa logging macro:

#ifdef DEBUG
// DLOG takes a format argument (which must begin %s) and 0 or more args:
// DLOG(@"%s");
// DLOG(@"%s: %d", x);
#define DLOG(fmt, ...) NSLog(fmt, __PRETTY_FUNCTION__, ##__VA_ARGS__)
#else
#define DLOG(...)
#endif

A drawback with this macro is that it requires you to add a “%s” to all your log statements, e.g. DLOG(@”%s foo”); instead of NSLog(@”foo”);. So it’s not a drop-in replacement for NSLog.

Let’s try to fix that.

#define DLog(fmt, ...) NSLog((@"%s " fmt), __PRETTY_FUNCTION__, ##__VA_ARGS__);

Now you can do DLog(@”foo”); as well as pass in parameters like DLog(@”value: %d”, x);

One final touch is to add the line number:

#define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);

Now if you’re lazy and you want to know that execution has reached a particular line, you can just use DLog();

So here is the complete listing of the macro:

// DLog is almost a drop-in replacement for NSLog
// DLog();
// DLog(@"here");
// DLog(@"value: %d", x);
// Unfortunately this doesn't work DLog(aStringVariable); you have to do this instead DLog(@"%@", aStringVariable);
#ifdef DEBUG
#	define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#	define DLog(...)
#endif

// ALog always displays output regardless of the DEBUG setting
#define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);

Set the DEBUG variable in your project as described previously. Update: If your project is 3.0 you may need to set the DEBUG variable this way.

I’m sure there will be further improvements to this. Comments are open as always.

written by Nick \\ tags:

Mar 06

A simple method to debug your iPhone application code is to use NSLog() statements at strategic points. I often find myself printing out the response from server calls using NSLog. Even after the iPhone client development is done, these log statements can be useful to debug server issues. But you obviously don’t want to fill the console log in your released code.

To skip lines of code you don’t want to include in the release build you can use a preprocessor instruction:

#ifdef DEBUG
NSLog(@"Server Response: %@", response);
#endif

Unfortunately Xcode does not automagically define DEBUG when you do a debug build. So you have to add a definition to your project target.

1. Be sure you select Debug in the Configuration drop down.

2. Select Settings Defined at This Level in the second drop down to reduce the number of fields shown.

3. Now click the tool button in the bottom left corner and select Add User-Defined Setting from the menu.

4. Enter the following name value pair:
OTHER_CFLAGS    -DDEBUG=1

Now when you build and run in debug configuration you will see the log messages, but those lines of code will not be included in your release builds.

Update: Use GCC_PREPROCESSOR_DEFINITIONS instead of OTHER_CFLAGS. See comments below.

Update 2: See this post for a vastly improved method: The Evolution of a Replacement for NSLog

Update 3: See this post if you’re working with an SDK 3.0 project: How To Add DEBUG Flag In A 3.0 Project In Xcode

written by Nick \\ tags:

Oct 07

The first rule of performance tuning is to measure your performance. If you don’t measure it, you don’t know if you’ve made any progress or if you’ve made it worse.

Here’s some very basic sample code you can use around the code you want to measure:

NSLog(@"Begin <your method name>");
NSDate *startTime = [NSDate date];

// Do something useful here

NSTimeInterval elapsedTime = [startTime timeIntervalSinceNow];
NSLog(@"End <your method name>");
NSLog([NSString stringWithFormat:@"Elapsed time: %f", -elapsedTime]);

Note that NSTimeInterval is specified in seconds and it has a sub-millisecond resolution. If your code segment executes in less than a millisecond then you should run your code in a loop and measure the elapsed time across 100 or 1,000 iterations to get meaningful measurements.

Important: Make sure that you do your performance testing on the actual device and not in the simulator. Some operations are 10x slower on a device than in the simulator, while others are actually faster on the device.

The NSLog statements will show up in the Xcode Console window if you launch the app on the device from Xcode. You can also view log statements as warnings in the Organizer window in the Console tab.

 

written by Nick \\ tags: , ,