Skip to content

Archive

Tag: NSLog

This article is about using NSLog with XCode unit testing; for a quick introduction to NSLog, follow the link.

Some programmers use traces to debug. Other programmers use breakpoints. Finally, some programmers use both.

So I wanted to trace some output while running (what formally looked like) a unit test, and I didn’t quite find my NSLog output in the console. Then I read this thread on cocoabuilder.com

Where are the traces gone?

OK, instead of [Run > Console], open [Build > Build Results]. Now, whether a build failed or succeeded, this window works like a tree and has lots of items marked green (success) or red (failed!). Pick any of these items, on the right hand side, there is an icon for ‘more details’  or ‘text output’.

Pressing this icon will display the actual console output, along with your NSLog output.

So if you have a test suite X, drill down to the ‘Run test suite X’ item and expand the text output; this will display information for the matching run, along with your NSLog output

Chasing bugs isn’t always fun. When I got back to it after my first release, a few useful tools went missing and I didn’t immediately realize it. Here’s at least 3 tools/functions that you want to use in development:

  • NSAssert – get your code to fail fast and report when a bad condition occurs.
  • NSLog – dump text to the console
  • NSZombieEnabled – an environment variable that gets really helpful to detect zombies.

You might want to disable NSAssert. NSAssert will crash your app immediately when a condition isn’t met (useful in development). I feel biased about disabling NSAssert when in production mode. First, none of your asserts should ever fire by the time you release to the app store. Second, if an assert did/should fire, what next… Well, either you have a fallback mechanism ready – then you probably don’t want a live app to crash at that point. Or (common case) you don’t have a fallback mechanism. Then what? Most likely, your app will crash anyway. So you will get a crash log, but that crash log might tell you nothing about the error.

You surely want to disable NSLog. NSLog is just wasted processing time and shouldn’t be used in production mode.

I’m not sure whether NSZombieEnabled needs to be disabled before shipping to the app store. My guess is, it’s a runtime setting, shouldn’t be passed to your app when running from iOS desktop anyways…

Code to conditionally disable NSAssert, NSLog.

If you put the following code in your *.pch file, NSAssert, NSLog get disabled when building for release – as a side effect of the __OPTIMIZE__ flag being set.

#ifndef __OPTIMIZE__

#define NSLog(…) NSLog(__VA_ARGS__)

#else

#undef NSAssert

#undef NSAssert1

#undef NSAssert2

#define NSAssert(x,y)

#define NSAssert1(x,y,z)

#define NSAssert2(x,y,z,w)

//

#define NSLog(…) {}

#endif

Beware that you don’t put code that actually does something inside your asserts. That probably looks like a no brainer, but it’s actually quite easy to accidentally paste a function call that returns a BOOL inside an assert, and forget that once the assert is removed, the call is being skipped. By any means, if you use the above code, regression test at least once in release mode before shipping your app.

Macros are evil! Welcome to the dark side.

NSLog is a trace function. In short this emulates the concision of System.out.println() (java) without losing anything to the elegance of trace() (AS2/3).

Using NSLog

There seems to be scores of forum posts about NSLog not working with XCode, and even fixes for the problem. But hey, for the rest of us, NSLog works perfectly with XCode and CocoaTouch. You include something like:

// Print 'Hello 1st console output' to the console
NSLog("Hello %ist console output",1);

While a project is open, select Run >> Console to view NSLog output.

NSLog with Macros

I suggested earlier that there might be reasonable uses of macros. I’m reading somewhere that the C preprocessor is a *steaming turd*, so let’s have it. Try this:

#define print(...) NSLog(@__VA_ARGS__)
// Print 'Hello 2nd console output' to the console
print("Hello %ind console output",2);

The little more than notational convenience here is that you can easily and efficiently remove all logs from a production build by commenting out NSLog(@__VA_ARGS__) in the macro definition.

Removing annoying log output

By default, NSLog will output information about the current process along with a time stamp for every output from NSLog. Usually this is clutter. If you dump the “JANSLogHack” code in an .m file associated with your project, that will remove all this stuff. This is gracefully provided by Jens Ayton as a public domain utility.

#ifndef NDEBUG
#import <Foundation/Foundation.h>
#import <stdio.h>

extern void _NSSetLogCStringFunction(void (*)(const char *string, unsigned length, BOOL withSyslogBanner));

static void PrintNSLogMessage(const char *string, unsigned length, BOOL withSyslogBanner){
   puts(string);
}

static void HackNSLog(void) __attribute__((constructor));
static void HackNSLog(void){
  _NSSetLogCStringFunction(PrintNSLogMessage);
}
#endif

Stripping traces off release builds

Keeping traces in releases builds isn’t a good idea as they can seriously slow down code execution. I’m not covering this here, but you could check this article from the iphone development blog.

How about using the debugger

You might be inspired to try the XCode debugger in many situations where you would trace stuff. The debugger lets you set breakpoints to interrupt your program while running and – seriously useful – check the content of variables and objects by hovering over your source with the mouse. I often find this significantly faster and cleaner than using traces.