Working with Assertions to Debug your Apps

Assertions provide a mechanism to catch errors by checking a condition statement(s) and throwing an exception if the condition check fails. Assertions are implemented as macros that you can use to evaluate conditions at various points in your application. Often times assertions are used as a sanity check to verify variables contain the values, or range of values expected.

As an example, I’ve used assertions during early development phases when working with web services – as I was writing the iPhone app another team was developing web-services and programming interfaces to the same. As we were getting parameter passing to work, assertions were very helpful to check expected values.

Using Assertions in Objective-C Methods

Two categories of assertions are available when writing iOS apps, assertions for use within Objective-C methods, and assertions that are used inside a C-based function. Let’s start with method based assertions:

NSAssert and its kin – NSAssert1, NSAssert2, etc – are used inside methods. NSAssert is defined as follows:

#define NSAssert(condition, desc)

condition is the expression we are interested to evaluate and desc is the message displayed on the console when the assertion fails.

Looking a little further, in NSException.h we can see the full definition:

#define NSAssert(condition, desc, ...) \
    do {			\
	if (!(condition)) {	\
	    [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \
		object:self file:[NSString stringWithUTF8String:__FILE__] \
	    	lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \
	}			\
    } while(0)

Notice the check for !(condition) – if the condition check fails, an exception is generated with information about the file where the error occurred and the line number within that file.

The following example shows how you might go about using an assertion:

- (void) sanityCheck:(int)x
  // Other code here...
  NSAssert((x > 0) && (x < 100), @"x is not within the acceptable range of 1-99");

If the value of x is not between 1 and 99, an exception will be generated and the following output will be shown in the console:

In addition to the basic NSAssert macro, there are macros available that enable additional information to be output into the description field. For example, the macro below takes an additional parameter, arg1, that can be used to show the value of a variable in the output:

#define NSAssert1(condition, desc, arg1)

NSAssert1((x > 0) && (x < 100), @"x is: %i, acceptable range is: 1-99", x);

The output is shown below:

Taking this one step further, the example below shows NSAssert3, which will accept three arguments:

#define MIN_VALUE 1 
#define MAX_VALUE 99
NSAssert3((x > 0) && (x < 100), @"x is:%i, acceptable range is:%d-%d", x, MIN_VALUE, MAX_VALUE);

The output is essentially the same as in the previous example, however, the values for the range as derived from the min and max value definitions.

The entire range of macros for Objective-C methods is: NSAssert, NSAssert1, NSAssert2, NSAssert3, NSAssert4 and NSAssert5.

Using Assertions in C Functions

Assertions can also be used within C functions and work in a very similar fashion as with Objective-C methods. There is a range of macros with the naming conventions NSCAssert, NSCAssert1, NSCAssert2, etc. NSCAssert looks as follows:

NSCAssert(condition, NSString *description)

You use these macros in C functions in the same manner as shown above.

Disabling Assertions

I typically use assertions during development only – you can disable assertions by adding the preprocessor macro NS_BLOCK_ASSERTIONS in the build settings for your Release builds.

The screenshot below shows how to configure the Build Settings inside Xcode 4 to add NS_BLOCK_ASSERTIONS.

  1. Just be careful where you use them. I have some sample code from an extremely important player in the iPhone SDK game which has assertions that trigger under conditions that can only be described as “always, except for the pathological data set supplied with the example”.

Comments are closed.