Debugging with Three20

TTDebug.h contains a set of useful debug tools, including priority-based logging and debug-only assertions. These macros deprecate the original Three20 TTLOG macros and Xcode NSLog methods that were difficult to disable (and tended to cause an unending stream of log messages). The goal of the new logging framework is to make it easy to see the logs you care about.

Table of Contents

What is logging?

Writing text to a console or file, at it's simplest. In Xcode, we use the NSLog method to write to the Xcode log, visible when running an application with the debugger attached.

Three20 adds an extra layer that makes it easier to disable logging when you ship your app, selectively show certain logging messages, or make assertions only in development builds of your app.

How to enable Three20 logging

Define a DEBUG preprocessor definition in the GCC_PREPROCESSOR_DEFINITIONS option in your debug target settings. You shouldn't define this for your release build. If you define DEBUG in the build you submit to the App Store, your app will be rejected.

Add the preprocessor definitions

How to write to the log

The logging system introduces a new set of priority-based macros.

TTDERROR(text, ...)    // Priority level 1
TTDWARNING(text, ...)  // Priority level 3
TTDINFO(text, ...)     // Priority level 5
TTDPRINT(text, ...)    // Always prints

What does priority mean?

The log message will only be written if the log's priority level is below the TTMAXLOGLEVEL. So if the max log level is 3, then no logging messages with a priority level higher than that will be displayed (i.e. TTDINFO).

If you don't explicitly set TTMAXLOGLEVEL, the default is set to TTLOGLEVEL_WARNING, meaning only warning and error logs will be displayed.

The standard output of TTDPRINT looks something like this:

TTDPRINT(@"Is this thing on?");
2009-11-20 13:46:49.613 AppName /path/to/file/Filename.m(86): Is this thing on?

Conditional Logging

New since Dec 5, 2009

Conditional logging allows you to produce output only when a certain condition is met. A quick example:

TTDCONDITIONLOG(TTDFLAG_URLREQUEST, @"Request parameters: %@", request.parameters)

This will only produce the log if the flag TTDFLAG_URLREQUEST is set to a non-zero value. All the available conditional log flags are listed and defined in TTDebugFlags.h. To turn them on, edit their values in your local copy of this file.

Debug-only assertions

It can often be useful to validate the parameters passed into a method. The method may return preemptively if the parameters are invalid or missing. This is a way to fail somewhat gracefully. When you're actively developing code, however, it would be ideal if the method could shout out to you "hey, listen!", letting you know that the method is being abused.

This is where TTDASSERT comes in. It works like a regular assertion - in that if you pass a zero-value to it, it lets you know - but it has the nice benefit of not crashing your app. Instead, it jumps straight into the debugger at the line that caused the issue. It's also gracefully compiled away in release builds, so your shipping product will never know they existed.

Consider this example:

-(void)safeAddSubview:(UIView*)view {
  TTDASSERT(nil != view);
  if (nil == view) {
    return;
  }
  [self addSubview:view];
}

Let's say we then call [myView safeAddSubview:nil]. In debug builds of the app, calling this will now toss us into the debugger at the culprit line and output a quick log explaining the problem.

2009-11-20 14:10:53.096 AppName...
  /path/to/file/Filename.m(86): TTDASSERT failed: nil != view

Some things to note about debug assertions.

Debug assertions will only fire in the simulator. On debug device builds the assertion will fail, output a log message, and then carry forward.

If you're in the simulator and debug assertions aren't starting up the debugger, it's likely because you're not starting the app with gdb attached. To ensure that you always attach gdb to the app when you start it, use the hot-key Cmd+Y instead of Cmd+Enter.

blog comments powered by Disqus